1 | const isArray = Array.isArray;
|
2 | const isObject = (value) => value.constructor === Object;
|
3 | const slice = Array.prototype.slice;
|
4 | const sameValue = Object.is;
|
5 | const isNative = (value) => (value + '').includes('[native code]');
|
6 | const HIDDEN_CONTENT = '{…}';
|
7 |
|
8 | const buildPrefix = (indent) => {
|
9 | let output = '\n';
|
10 |
|
11 | for (let i = 0; i < indent; i++) {
|
12 | output += ' ';
|
13 | }
|
14 |
|
15 | return output;
|
16 | };
|
17 |
|
18 | const numberString = (value) => {
|
19 | if (!isFinite(value)) {
|
20 | return value + '';
|
21 | }
|
22 |
|
23 | if (sameValue(value, -0)) {
|
24 | return '-0';
|
25 | }
|
26 |
|
27 | return value.toLocaleString('default', {
|
28 | maximumFractionDigits: 20
|
29 | });
|
30 | };
|
31 |
|
32 | const bigIntString = (value) => {
|
33 | if (sameValue(value, -0n)) {
|
34 | return '-0n';
|
35 | }
|
36 |
|
37 | return Number(value)
|
38 | .toLocaleString('default', {
|
39 | maximumFractionDigits: 20
|
40 | }) + 'n';
|
41 | };
|
42 |
|
43 | const symbolString = (value) => {
|
44 | if (value.description !== undefined) {
|
45 | return 'Symbol(' + value.description + ')';
|
46 | }
|
47 |
|
48 | return value.toString();
|
49 | };
|
50 |
|
51 | const functionString = (value) => {
|
52 | const string = value + '';
|
53 |
|
54 | if (value.name !== '' && isNative(string)) {
|
55 | return value.name;
|
56 | }
|
57 |
|
58 | return string
|
59 | .slice(0, string.indexOf('{'))
|
60 | .replace('function', 'ƒ')
|
61 | .replace('ƒ(', 'ƒ (') + HIDDEN_CONTENT;
|
62 | };
|
63 |
|
64 | const nonNativeInstanceString = (value) => {
|
65 | if (value.toString) {
|
66 | const string = value.toString();
|
67 |
|
68 | if (string !== '[object Object]') {
|
69 | return string;
|
70 | }
|
71 | }
|
72 |
|
73 | return `[object ${value.constructor.name}]`;
|
74 | };
|
75 |
|
76 | const addValue = (output, value, key, indentString, type, settings, isAnArray) => {
|
77 | if (output.length !== 1) {
|
78 | output += settings.separator;
|
79 | }
|
80 | else if (!isAnArray && !settings.beautify) {
|
81 | output += ' ';
|
82 | }
|
83 |
|
84 | if (isAnArray || type === 'set') {
|
85 | return output + (
|
86 | (output.length === 1 || value.charAt(0) !== '{') ?
|
87 | indentString :
|
88 | settings.space
|
89 | ) + value;
|
90 | }
|
91 |
|
92 | return output + indentString + '"' + key + '": ' + value;
|
93 | };
|
94 |
|
95 | const stringify = (value, indent, settings, type) => {
|
96 | const indentString = settings.beautify ? buildPrefix(indent) : '';
|
97 | const isAnArray = type === 'array' || type === 'typedarray' || type === 'arraylike';
|
98 | let output = isAnArray ? '[' : '{';
|
99 |
|
100 | if (isAnArray || type === 'object') {
|
101 | for (const key in value) {
|
102 | output = addValue(
|
103 | output,
|
104 | processValue(value[key], indent, settings),
|
105 | key,
|
106 | indentString,
|
107 | type,
|
108 | settings,
|
109 | isAnArray
|
110 | );
|
111 | }
|
112 | }
|
113 | else {
|
114 | for (const key of value) {
|
115 | output = addValue(
|
116 | output,
|
117 | processValue(type === 'map' ? key[1] : key, indent, settings),
|
118 | key[0],
|
119 | indentString,
|
120 | type,
|
121 | settings,
|
122 | isAnArray
|
123 | );
|
124 | }
|
125 | }
|
126 |
|
127 | if (output.length !== 1) {
|
128 | if (settings.beautify) {
|
129 | output += buildPrefix(indent - 1);
|
130 | }
|
131 | else if (!isAnArray) {
|
132 | output += ' ';
|
133 | }
|
134 | }
|
135 |
|
136 | return output + (isAnArray ? ']' : '}');
|
137 | };
|
138 |
|
139 | const getType = (value) => {
|
140 | const type = typeof value;
|
141 |
|
142 | if (type !== 'object') {
|
143 | return type;
|
144 | }
|
145 |
|
146 | if (value !== null && value !== undefined) {
|
147 | if (value instanceof String) {
|
148 | return 'string';
|
149 | }
|
150 |
|
151 | if (isArray(value)) {
|
152 | return 'array';
|
153 | }
|
154 |
|
155 | if (isObject(value) || value.toJSON && !(value instanceof Date)) {
|
156 | if (value.length !== undefined) {
|
157 | return 'arraylike';
|
158 | }
|
159 |
|
160 | return 'object';
|
161 | }
|
162 |
|
163 | if (value instanceof Set) {
|
164 | return 'set';
|
165 | }
|
166 |
|
167 | if (value instanceof Map) {
|
168 | return 'map';
|
169 | }
|
170 |
|
171 | if (value instanceof WeakSet) {
|
172 | return 'weakset';
|
173 | }
|
174 |
|
175 | if (value instanceof WeakMap) {
|
176 | return 'weakmap';
|
177 | }
|
178 |
|
179 | if (
|
180 | value instanceof Int8Array ||
|
181 | value instanceof Uint8Array ||
|
182 | value instanceof Uint8ClampedArray ||
|
183 | value instanceof Int16Array ||
|
184 | value instanceof Uint16Array ||
|
185 | value instanceof Int32Array ||
|
186 | value instanceof Uint32Array ||
|
187 | value instanceof BigInt64Array ||
|
188 | value instanceof BigUint64Array ||
|
189 | value instanceof Float32Array ||
|
190 | value instanceof Float64Array
|
191 | ) {
|
192 | return 'typedarray';
|
193 | }
|
194 |
|
195 | if (value.constructor !== undefined && !isNative(value.constructor)) {
|
196 | return 'constructor';
|
197 | }
|
198 | }
|
199 | };
|
200 |
|
201 | const processValue = (value, indent, settings) => {
|
202 | const type = getType(value);
|
203 |
|
204 | switch (type) {
|
205 | case 'string':
|
206 | return `"${value}"`;
|
207 | case 'number':
|
208 | return numberString(value);
|
209 | case 'bigint':
|
210 | return bigIntString(value);
|
211 | case 'symbol':
|
212 | return symbolString(value);
|
213 | case 'function':
|
214 | return functionString(value);
|
215 | case 'array':
|
216 | case 'object':
|
217 | case 'arraylike':
|
218 | return stringify(value, indent + 1, settings, type);
|
219 | case 'typedarray':
|
220 | case 'set':
|
221 | case 'map':
|
222 | return value.constructor.name + ' ' + stringify(value, indent + 1, settings, type);
|
223 | case 'weakset':
|
224 | case 'weakmap':
|
225 | return value.constructor.name + ' ' + HIDDEN_CONTENT;
|
226 | case 'constructor':
|
227 | return nonNativeInstanceString(value);
|
228 | default:
|
229 | return value + '';
|
230 | }
|
231 | };
|
232 |
|
233 | const defaultSettings = {
|
234 | space: '',
|
235 | separator: ', ',
|
236 | beautify: false
|
237 | };
|
238 |
|
239 | const beautifySettings = {
|
240 | space: ' ',
|
241 | separator: ',',
|
242 | beautify: true
|
243 | };
|
244 |
|
245 |
|
246 |
|
247 |
|
248 |
|
249 |
|
250 |
|
251 |
|
252 |
|
253 |
|
254 |
|
255 |
|
256 |
|
257 |
|
258 |
|
259 |
|
260 |
|
261 |
|
262 |
|
263 |
|
264 |
|
265 |
|
266 |
|
267 |
|
268 |
|
269 |
|
270 |
|
271 |
|
272 |
|
273 |
|
274 |
|
275 |
|
276 |
|
277 |
|
278 |
|
279 |
|
280 |
|
281 |
|
282 |
|
283 |
|
284 |
|
285 |
|
286 |
|
287 |
|
288 |
|
289 |
|
290 |
|
291 |
|
292 |
|
293 |
|
294 |
|
295 |
|
296 |
|
297 |
|
298 | export default function displayValue(value, settings = {}) {
|
299 | return processValue(value, 0, settings.beautify === true ? beautifySettings : defaultSettings);
|
300 | };
|