479 lines
13 KiB
JavaScript
479 lines
13 KiB
JavaScript
'use strict';
|
|
|
|
Object.defineProperty(exports, '__esModule', {
|
|
value: true
|
|
});
|
|
exports.default = exports.DEFAULT_OPTIONS = void 0;
|
|
exports.format = format;
|
|
exports.plugins = void 0;
|
|
var _ansiStyles = _interopRequireDefault(require('ansi-styles'));
|
|
var _collections = require('./collections');
|
|
var _AsymmetricMatcher = _interopRequireDefault(
|
|
require('./plugins/AsymmetricMatcher')
|
|
);
|
|
var _DOMCollection = _interopRequireDefault(require('./plugins/DOMCollection'));
|
|
var _DOMElement = _interopRequireDefault(require('./plugins/DOMElement'));
|
|
var _Immutable = _interopRequireDefault(require('./plugins/Immutable'));
|
|
var _ReactElement = _interopRequireDefault(require('./plugins/ReactElement'));
|
|
var _ReactTestComponent = _interopRequireDefault(
|
|
require('./plugins/ReactTestComponent')
|
|
);
|
|
function _interopRequireDefault(obj) {
|
|
return obj && obj.__esModule ? obj : {default: obj};
|
|
}
|
|
/**
|
|
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*/
|
|
|
|
/* eslint-disable local/ban-types-eventually */
|
|
|
|
const toString = Object.prototype.toString;
|
|
const toISOString = Date.prototype.toISOString;
|
|
const errorToString = Error.prototype.toString;
|
|
const regExpToString = RegExp.prototype.toString;
|
|
|
|
/**
|
|
* Explicitly comparing typeof constructor to function avoids undefined as name
|
|
* when mock identity-obj-proxy returns the key as the value for any key.
|
|
*/
|
|
const getConstructorName = val =>
|
|
(typeof val.constructor === 'function' && val.constructor.name) || 'Object';
|
|
|
|
/* global window */
|
|
/** Is val is equal to global window object? Works even if it does not exist :) */
|
|
const isWindow = val => typeof window !== 'undefined' && val === window;
|
|
const SYMBOL_REGEXP = /^Symbol\((.*)\)(.*)$/;
|
|
const NEWLINE_REGEXP = /\n/gi;
|
|
class PrettyFormatPluginError extends Error {
|
|
constructor(message, stack) {
|
|
super(message);
|
|
this.stack = stack;
|
|
this.name = this.constructor.name;
|
|
}
|
|
}
|
|
function isToStringedArrayType(toStringed) {
|
|
return (
|
|
toStringed === '[object Array]' ||
|
|
toStringed === '[object ArrayBuffer]' ||
|
|
toStringed === '[object DataView]' ||
|
|
toStringed === '[object Float32Array]' ||
|
|
toStringed === '[object Float64Array]' ||
|
|
toStringed === '[object Int8Array]' ||
|
|
toStringed === '[object Int16Array]' ||
|
|
toStringed === '[object Int32Array]' ||
|
|
toStringed === '[object Uint8Array]' ||
|
|
toStringed === '[object Uint8ClampedArray]' ||
|
|
toStringed === '[object Uint16Array]' ||
|
|
toStringed === '[object Uint32Array]'
|
|
);
|
|
}
|
|
function printNumber(val) {
|
|
return Object.is(val, -0) ? '-0' : String(val);
|
|
}
|
|
function printBigInt(val) {
|
|
return String(`${val}n`);
|
|
}
|
|
function printFunction(val, printFunctionName) {
|
|
if (!printFunctionName) {
|
|
return '[Function]';
|
|
}
|
|
return `[Function ${val.name || 'anonymous'}]`;
|
|
}
|
|
function printSymbol(val) {
|
|
return String(val).replace(SYMBOL_REGEXP, 'Symbol($1)');
|
|
}
|
|
function printError(val) {
|
|
return `[${errorToString.call(val)}]`;
|
|
}
|
|
|
|
/**
|
|
* The first port of call for printing an object, handles most of the
|
|
* data-types in JS.
|
|
*/
|
|
function printBasicValue(val, printFunctionName, escapeRegex, escapeString) {
|
|
if (val === true || val === false) {
|
|
return `${val}`;
|
|
}
|
|
if (val === undefined) {
|
|
return 'undefined';
|
|
}
|
|
if (val === null) {
|
|
return 'null';
|
|
}
|
|
const typeOf = typeof val;
|
|
if (typeOf === 'number') {
|
|
return printNumber(val);
|
|
}
|
|
if (typeOf === 'bigint') {
|
|
return printBigInt(val);
|
|
}
|
|
if (typeOf === 'string') {
|
|
if (escapeString) {
|
|
return `"${val.replace(/"|\\/g, '\\$&')}"`;
|
|
}
|
|
return `"${val}"`;
|
|
}
|
|
if (typeOf === 'function') {
|
|
return printFunction(val, printFunctionName);
|
|
}
|
|
if (typeOf === 'symbol') {
|
|
return printSymbol(val);
|
|
}
|
|
const toStringed = toString.call(val);
|
|
if (toStringed === '[object WeakMap]') {
|
|
return 'WeakMap {}';
|
|
}
|
|
if (toStringed === '[object WeakSet]') {
|
|
return 'WeakSet {}';
|
|
}
|
|
if (
|
|
toStringed === '[object Function]' ||
|
|
toStringed === '[object GeneratorFunction]'
|
|
) {
|
|
return printFunction(val, printFunctionName);
|
|
}
|
|
if (toStringed === '[object Symbol]') {
|
|
return printSymbol(val);
|
|
}
|
|
if (toStringed === '[object Date]') {
|
|
return isNaN(+val) ? 'Date { NaN }' : toISOString.call(val);
|
|
}
|
|
if (toStringed === '[object Error]') {
|
|
return printError(val);
|
|
}
|
|
if (toStringed === '[object RegExp]') {
|
|
if (escapeRegex) {
|
|
// https://github.com/benjamingr/RegExp.escape/blob/main/polyfill.js
|
|
return regExpToString.call(val).replace(/[\\^$*+?.()|[\]{}]/g, '\\$&');
|
|
}
|
|
return regExpToString.call(val);
|
|
}
|
|
if (val instanceof Error) {
|
|
return printError(val);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Handles more complex objects ( such as objects with circular references.
|
|
* maps and sets etc )
|
|
*/
|
|
function printComplexValue(
|
|
val,
|
|
config,
|
|
indentation,
|
|
depth,
|
|
refs,
|
|
hasCalledToJSON
|
|
) {
|
|
if (refs.indexOf(val) !== -1) {
|
|
return '[Circular]';
|
|
}
|
|
refs = refs.slice();
|
|
refs.push(val);
|
|
const hitMaxDepth = ++depth > config.maxDepth;
|
|
const min = config.min;
|
|
if (
|
|
config.callToJSON &&
|
|
!hitMaxDepth &&
|
|
val.toJSON &&
|
|
typeof val.toJSON === 'function' &&
|
|
!hasCalledToJSON
|
|
) {
|
|
return printer(val.toJSON(), config, indentation, depth, refs, true);
|
|
}
|
|
const toStringed = toString.call(val);
|
|
if (toStringed === '[object Arguments]') {
|
|
return hitMaxDepth
|
|
? '[Arguments]'
|
|
: `${min ? '' : 'Arguments '}[${(0, _collections.printListItems)(
|
|
val,
|
|
config,
|
|
indentation,
|
|
depth,
|
|
refs,
|
|
printer
|
|
)}]`;
|
|
}
|
|
if (isToStringedArrayType(toStringed)) {
|
|
return hitMaxDepth
|
|
? `[${val.constructor.name}]`
|
|
: `${
|
|
min
|
|
? ''
|
|
: !config.printBasicPrototype && val.constructor.name === 'Array'
|
|
? ''
|
|
: `${val.constructor.name} `
|
|
}[${(0, _collections.printListItems)(
|
|
val,
|
|
config,
|
|
indentation,
|
|
depth,
|
|
refs,
|
|
printer
|
|
)}]`;
|
|
}
|
|
if (toStringed === '[object Map]') {
|
|
return hitMaxDepth
|
|
? '[Map]'
|
|
: `Map {${(0, _collections.printIteratorEntries)(
|
|
val.entries(),
|
|
config,
|
|
indentation,
|
|
depth,
|
|
refs,
|
|
printer,
|
|
' => '
|
|
)}}`;
|
|
}
|
|
if (toStringed === '[object Set]') {
|
|
return hitMaxDepth
|
|
? '[Set]'
|
|
: `Set {${(0, _collections.printIteratorValues)(
|
|
val.values(),
|
|
config,
|
|
indentation,
|
|
depth,
|
|
refs,
|
|
printer
|
|
)}}`;
|
|
}
|
|
|
|
// Avoid failure to serialize global window object in jsdom test environment.
|
|
// For example, not even relevant if window is prop of React element.
|
|
return hitMaxDepth || isWindow(val)
|
|
? `[${getConstructorName(val)}]`
|
|
: `${
|
|
min
|
|
? ''
|
|
: !config.printBasicPrototype && getConstructorName(val) === 'Object'
|
|
? ''
|
|
: `${getConstructorName(val)} `
|
|
}{${(0, _collections.printObjectProperties)(
|
|
val,
|
|
config,
|
|
indentation,
|
|
depth,
|
|
refs,
|
|
printer
|
|
)}}`;
|
|
}
|
|
function isNewPlugin(plugin) {
|
|
return plugin.serialize != null;
|
|
}
|
|
function printPlugin(plugin, val, config, indentation, depth, refs) {
|
|
let printed;
|
|
try {
|
|
printed = isNewPlugin(plugin)
|
|
? plugin.serialize(val, config, indentation, depth, refs, printer)
|
|
: plugin.print(
|
|
val,
|
|
valChild => printer(valChild, config, indentation, depth, refs),
|
|
str => {
|
|
const indentationNext = indentation + config.indent;
|
|
return (
|
|
indentationNext +
|
|
str.replace(NEWLINE_REGEXP, `\n${indentationNext}`)
|
|
);
|
|
},
|
|
{
|
|
edgeSpacing: config.spacingOuter,
|
|
min: config.min,
|
|
spacing: config.spacingInner
|
|
},
|
|
config.colors
|
|
);
|
|
} catch (error) {
|
|
throw new PrettyFormatPluginError(error.message, error.stack);
|
|
}
|
|
if (typeof printed !== 'string') {
|
|
throw new Error(
|
|
`pretty-format: Plugin must return type "string" but instead returned "${typeof printed}".`
|
|
);
|
|
}
|
|
return printed;
|
|
}
|
|
function findPlugin(plugins, val) {
|
|
for (let p = 0; p < plugins.length; p++) {
|
|
try {
|
|
if (plugins[p].test(val)) {
|
|
return plugins[p];
|
|
}
|
|
} catch (error) {
|
|
throw new PrettyFormatPluginError(error.message, error.stack);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
function printer(val, config, indentation, depth, refs, hasCalledToJSON) {
|
|
const plugin = findPlugin(config.plugins, val);
|
|
if (plugin !== null) {
|
|
return printPlugin(plugin, val, config, indentation, depth, refs);
|
|
}
|
|
const basicResult = printBasicValue(
|
|
val,
|
|
config.printFunctionName,
|
|
config.escapeRegex,
|
|
config.escapeString
|
|
);
|
|
if (basicResult !== null) {
|
|
return basicResult;
|
|
}
|
|
return printComplexValue(
|
|
val,
|
|
config,
|
|
indentation,
|
|
depth,
|
|
refs,
|
|
hasCalledToJSON
|
|
);
|
|
}
|
|
const DEFAULT_THEME = {
|
|
comment: 'gray',
|
|
content: 'reset',
|
|
prop: 'yellow',
|
|
tag: 'cyan',
|
|
value: 'green'
|
|
};
|
|
const DEFAULT_THEME_KEYS = Object.keys(DEFAULT_THEME);
|
|
|
|
// could be replaced by `satisfies` operator in the future: https://github.com/microsoft/TypeScript/issues/47920
|
|
const toOptionsSubtype = options => options;
|
|
const DEFAULT_OPTIONS = toOptionsSubtype({
|
|
callToJSON: true,
|
|
compareKeys: undefined,
|
|
escapeRegex: false,
|
|
escapeString: true,
|
|
highlight: false,
|
|
indent: 2,
|
|
maxDepth: Infinity,
|
|
maxWidth: Infinity,
|
|
min: false,
|
|
plugins: [],
|
|
printBasicPrototype: true,
|
|
printFunctionName: true,
|
|
theme: DEFAULT_THEME
|
|
});
|
|
exports.DEFAULT_OPTIONS = DEFAULT_OPTIONS;
|
|
function validateOptions(options) {
|
|
Object.keys(options).forEach(key => {
|
|
if (!Object.prototype.hasOwnProperty.call(DEFAULT_OPTIONS, key)) {
|
|
throw new Error(`pretty-format: Unknown option "${key}".`);
|
|
}
|
|
});
|
|
if (options.min && options.indent !== undefined && options.indent !== 0) {
|
|
throw new Error(
|
|
'pretty-format: Options "min" and "indent" cannot be used together.'
|
|
);
|
|
}
|
|
if (options.theme !== undefined) {
|
|
if (options.theme === null) {
|
|
throw new Error('pretty-format: Option "theme" must not be null.');
|
|
}
|
|
if (typeof options.theme !== 'object') {
|
|
throw new Error(
|
|
`pretty-format: Option "theme" must be of type "object" but instead received "${typeof options.theme}".`
|
|
);
|
|
}
|
|
}
|
|
}
|
|
const getColorsHighlight = options =>
|
|
DEFAULT_THEME_KEYS.reduce((colors, key) => {
|
|
const value =
|
|
options.theme && options.theme[key] !== undefined
|
|
? options.theme[key]
|
|
: DEFAULT_THEME[key];
|
|
const color = value && _ansiStyles.default[value];
|
|
if (
|
|
color &&
|
|
typeof color.close === 'string' &&
|
|
typeof color.open === 'string'
|
|
) {
|
|
colors[key] = color;
|
|
} else {
|
|
throw new Error(
|
|
`pretty-format: Option "theme" has a key "${key}" whose value "${value}" is undefined in ansi-styles.`
|
|
);
|
|
}
|
|
return colors;
|
|
}, Object.create(null));
|
|
const getColorsEmpty = () =>
|
|
DEFAULT_THEME_KEYS.reduce((colors, key) => {
|
|
colors[key] = {
|
|
close: '',
|
|
open: ''
|
|
};
|
|
return colors;
|
|
}, Object.create(null));
|
|
const getPrintFunctionName = options =>
|
|
options?.printFunctionName ?? DEFAULT_OPTIONS.printFunctionName;
|
|
const getEscapeRegex = options =>
|
|
options?.escapeRegex ?? DEFAULT_OPTIONS.escapeRegex;
|
|
const getEscapeString = options =>
|
|
options?.escapeString ?? DEFAULT_OPTIONS.escapeString;
|
|
const getConfig = options => ({
|
|
callToJSON: options?.callToJSON ?? DEFAULT_OPTIONS.callToJSON,
|
|
colors: options?.highlight ? getColorsHighlight(options) : getColorsEmpty(),
|
|
compareKeys:
|
|
typeof options?.compareKeys === 'function' || options?.compareKeys === null
|
|
? options.compareKeys
|
|
: DEFAULT_OPTIONS.compareKeys,
|
|
escapeRegex: getEscapeRegex(options),
|
|
escapeString: getEscapeString(options),
|
|
indent: options?.min
|
|
? ''
|
|
: createIndent(options?.indent ?? DEFAULT_OPTIONS.indent),
|
|
maxDepth: options?.maxDepth ?? DEFAULT_OPTIONS.maxDepth,
|
|
maxWidth: options?.maxWidth ?? DEFAULT_OPTIONS.maxWidth,
|
|
min: options?.min ?? DEFAULT_OPTIONS.min,
|
|
plugins: options?.plugins ?? DEFAULT_OPTIONS.plugins,
|
|
printBasicPrototype: options?.printBasicPrototype ?? true,
|
|
printFunctionName: getPrintFunctionName(options),
|
|
spacingInner: options?.min ? ' ' : '\n',
|
|
spacingOuter: options?.min ? '' : '\n'
|
|
});
|
|
function createIndent(indent) {
|
|
return new Array(indent + 1).join(' ');
|
|
}
|
|
|
|
/**
|
|
* Returns a presentation string of your `val` object
|
|
* @param val any potential JavaScript object
|
|
* @param options Custom settings
|
|
*/
|
|
function format(val, options) {
|
|
if (options) {
|
|
validateOptions(options);
|
|
if (options.plugins) {
|
|
const plugin = findPlugin(options.plugins, val);
|
|
if (plugin !== null) {
|
|
return printPlugin(plugin, val, getConfig(options), '', 0, []);
|
|
}
|
|
}
|
|
}
|
|
const basicResult = printBasicValue(
|
|
val,
|
|
getPrintFunctionName(options),
|
|
getEscapeRegex(options),
|
|
getEscapeString(options)
|
|
);
|
|
if (basicResult !== null) {
|
|
return basicResult;
|
|
}
|
|
return printComplexValue(val, getConfig(options), '', 0, []);
|
|
}
|
|
const plugins = {
|
|
AsymmetricMatcher: _AsymmetricMatcher.default,
|
|
DOMCollection: _DOMCollection.default,
|
|
DOMElement: _DOMElement.default,
|
|
Immutable: _Immutable.default,
|
|
ReactElement: _ReactElement.default,
|
|
ReactTestComponent: _ReactTestComponent.default
|
|
};
|
|
exports.plugins = plugins;
|
|
var _default = format;
|
|
exports.default = _default;
|