116 lines
		
	
	
		
			2.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			116 lines
		
	
	
		
			2.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| const MAX_ARRAY_LENGTH = 10;
 | |
| const MAX_RECURSIVE_DEPTH = 2;
 | |
| /**
 | |
|  * Used to print values in error messages.
 | |
|  */
 | |
| 
 | |
| export function inspect(value) {
 | |
|   return formatValue(value, []);
 | |
| }
 | |
| 
 | |
| function formatValue(value, seenValues) {
 | |
|   switch (typeof value) {
 | |
|     case 'string':
 | |
|       return JSON.stringify(value);
 | |
| 
 | |
|     case 'function':
 | |
|       return value.name ? `[function ${value.name}]` : '[function]';
 | |
| 
 | |
|     case 'object':
 | |
|       return formatObjectValue(value, seenValues);
 | |
| 
 | |
|     default:
 | |
|       return String(value);
 | |
|   }
 | |
| }
 | |
| 
 | |
| function formatObjectValue(value, previouslySeenValues) {
 | |
|   if (value === null) {
 | |
|     return 'null';
 | |
|   }
 | |
| 
 | |
|   if (previouslySeenValues.includes(value)) {
 | |
|     return '[Circular]';
 | |
|   }
 | |
| 
 | |
|   const seenValues = [...previouslySeenValues, value];
 | |
| 
 | |
|   if (isJSONable(value)) {
 | |
|     const jsonValue = value.toJSON(); // check for infinite recursion
 | |
| 
 | |
|     if (jsonValue !== value) {
 | |
|       return typeof jsonValue === 'string'
 | |
|         ? jsonValue
 | |
|         : formatValue(jsonValue, seenValues);
 | |
|     }
 | |
|   } else if (Array.isArray(value)) {
 | |
|     return formatArray(value, seenValues);
 | |
|   }
 | |
| 
 | |
|   return formatObject(value, seenValues);
 | |
| }
 | |
| 
 | |
| function isJSONable(value) {
 | |
|   return typeof value.toJSON === 'function';
 | |
| }
 | |
| 
 | |
| function formatObject(object, seenValues) {
 | |
|   const entries = Object.entries(object);
 | |
| 
 | |
|   if (entries.length === 0) {
 | |
|     return '{}';
 | |
|   }
 | |
| 
 | |
|   if (seenValues.length > MAX_RECURSIVE_DEPTH) {
 | |
|     return '[' + getObjectTag(object) + ']';
 | |
|   }
 | |
| 
 | |
|   const properties = entries.map(
 | |
|     ([key, value]) => key + ': ' + formatValue(value, seenValues),
 | |
|   );
 | |
|   return '{ ' + properties.join(', ') + ' }';
 | |
| }
 | |
| 
 | |
| function formatArray(array, seenValues) {
 | |
|   if (array.length === 0) {
 | |
|     return '[]';
 | |
|   }
 | |
| 
 | |
|   if (seenValues.length > MAX_RECURSIVE_DEPTH) {
 | |
|     return '[Array]';
 | |
|   }
 | |
| 
 | |
|   const len = Math.min(MAX_ARRAY_LENGTH, array.length);
 | |
|   const remaining = array.length - len;
 | |
|   const items = [];
 | |
| 
 | |
|   for (let i = 0; i < len; ++i) {
 | |
|     items.push(formatValue(array[i], seenValues));
 | |
|   }
 | |
| 
 | |
|   if (remaining === 1) {
 | |
|     items.push('... 1 more item');
 | |
|   } else if (remaining > 1) {
 | |
|     items.push(`... ${remaining} more items`);
 | |
|   }
 | |
| 
 | |
|   return '[' + items.join(', ') + ']';
 | |
| }
 | |
| 
 | |
| function getObjectTag(object) {
 | |
|   const tag = Object.prototype.toString
 | |
|     .call(object)
 | |
|     .replace(/^\[object /, '')
 | |
|     .replace(/]$/, '');
 | |
| 
 | |
|   if (tag === 'Object' && typeof object.constructor === 'function') {
 | |
|     const name = object.constructor.name;
 | |
| 
 | |
|     if (typeof name === 'string' && name !== '') {
 | |
|       return name;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return tag;
 | |
| }
 |