-
-
Notifications
You must be signed in to change notification settings - Fork 2k
/
Copy pathDebug.js
111 lines (96 loc) · 3.17 KB
/
Debug.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import escape from 'lodash.escape';
import functionName from 'function.prototype.name';
import isString from 'is-string';
import isNumber from 'is-number-object';
import isCallable from 'is-callable';
import isBoolean from 'is-boolean-object';
import inspect from 'object-inspect';
import hasOwn from 'hasown';
import {
propsOfNode,
childrenOfNode,
} from './RSTTraversal';
import getAdapter from './getAdapter';
const booleanValue = Function.bind.call(Function.call, Boolean.prototype.valueOf);
export function typeName(node) {
const adapter = getAdapter();
if (adapter.displayNameOfNode) {
return getAdapter().displayNameOfNode(node) || 'Component';
}
return typeof node.type === 'function'
? (node.type.displayName || functionName(node.type) || 'Component')
: node.type;
}
export function spaces(n) {
return Array(n + 1).join(' ');
}
export function indent(depth, string) {
return string.split('\n').map((x) => `${spaces(depth)}${x}`).join('\n');
}
function propString(prop, options) {
if (isString(prop)) {
return inspect(String(prop), { quoteStyle: 'double' });
}
if (isNumber(prop)) {
return `{${inspect(Number(prop))}}`;
}
if (isBoolean(prop)) {
return `{${inspect(booleanValue(prop))}}`;
}
if (isCallable(prop)) {
return `{${inspect(prop)}}`;
}
if (typeof prop === 'object') {
if (options.verbose) {
return `{${inspect(prop)}}`;
}
return '{{...}}';
}
return `{[${inspect(prop)}]}`;
}
function propsString(node, options) {
const props = propsOfNode(node);
const keys = Object.keys(props).filter((x) => x !== 'children');
return keys.map((key) => `${key}=${propString(props[key], options)}`).join(' ');
}
function indentChildren(childrenStrs, indentLength) {
return childrenStrs.length
? `\n${childrenStrs.map((x) => indent(indentLength, x)).join('\n')}\n`
: '';
}
function isRSTNodeLike(node) {
return hasOwn(node, 'nodeType')
&& typeof node.nodeType === 'string'
&& hasOwn(node, 'type')
&& hasOwn(node, 'key')
&& hasOwn(node, 'ref')
&& hasOwn(node, 'instance')
&& hasOwn(node, 'rendered');
}
export function debugNode(node, indentLength = 2, options = {}) {
if (typeof node === 'string' || typeof node === 'number') return escape(node);
if (typeof node === 'function') {
const name = functionName(node);
return `[function${name ? ` ${name}` : ''}]`;
}
if (!node) return '';
const adapter = getAdapter();
if (!adapter.isValidElement(node) && !isRSTNodeLike(node)) {
return `{${inspect(node)}}`;
}
const childrenStrs = childrenOfNode(node)
.map((n) => debugNode(n, indentLength, options))
.filter(Boolean);
const type = typeName(node);
const props = options.ignoreProps ? '' : propsString(node, options);
const beforeProps = props ? ' ' : '';
const afterProps = childrenStrs.length
? '>'
: ' ';
const childrenIndented = indentChildren(childrenStrs, indentLength);
const nodeClose = childrenStrs.length ? `</${type}>` : '/>';
return `<${type}${beforeProps}${props}${afterProps}${childrenIndented}${nodeClose}`;
}
export function debugNodes(nodes, options = {}) {
return nodes.map((node) => debugNode(node, undefined, options)).join('\n\n\n');
}