index.js 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. // Taken from graphql-js
  2. // https://github.com/graphql/graphql-js/blob/main/src/jsutils/inspect.ts
  3. const MAX_RECURSIVE_DEPTH = 3;
  4. /**
  5. * Used to print values in error messages.
  6. */
  7. export function inspect(value) {
  8. return formatValue(value, []);
  9. }
  10. function formatValue(value, seenValues) {
  11. switch (typeof value) {
  12. case 'string':
  13. return JSON.stringify(value);
  14. case 'function':
  15. return value.name ? `[function ${value.name}]` : '[function]';
  16. case 'object':
  17. return formatObjectValue(value, seenValues);
  18. default:
  19. return String(value);
  20. }
  21. }
  22. function formatError(value) {
  23. // eslint-disable-next-line no-constant-condition
  24. if ((value.name = 'GraphQLError')) {
  25. return value.toString();
  26. }
  27. return `${value.name}: ${value.message};\n ${value.stack}`;
  28. }
  29. function formatObjectValue(value, previouslySeenValues) {
  30. if (value === null) {
  31. return 'null';
  32. }
  33. if (value instanceof Error) {
  34. if (value.name === 'AggregateError') {
  35. return (formatError(value) +
  36. '\n' +
  37. formatArray(value.errors, previouslySeenValues));
  38. }
  39. return formatError(value);
  40. }
  41. if (previouslySeenValues.includes(value)) {
  42. return '[Circular]';
  43. }
  44. const seenValues = [...previouslySeenValues, value];
  45. if (isJSONable(value)) {
  46. const jsonValue = value.toJSON();
  47. // check for infinite recursion
  48. if (jsonValue !== value) {
  49. return typeof jsonValue === 'string' ? jsonValue : formatValue(jsonValue, seenValues);
  50. }
  51. }
  52. else if (Array.isArray(value)) {
  53. return formatArray(value, seenValues);
  54. }
  55. return formatObject(value, seenValues);
  56. }
  57. function isJSONable(value) {
  58. return typeof value.toJSON === 'function';
  59. }
  60. function formatObject(object, seenValues) {
  61. const entries = Object.entries(object);
  62. if (entries.length === 0) {
  63. return '{}';
  64. }
  65. if (seenValues.length > MAX_RECURSIVE_DEPTH) {
  66. return '[' + getObjectTag(object) + ']';
  67. }
  68. const properties = entries.map(([key, value]) => key + ': ' + formatValue(value, seenValues));
  69. return '{ ' + properties.join(', ') + ' }';
  70. }
  71. function formatArray(array, seenValues) {
  72. if (array.length === 0) {
  73. return '[]';
  74. }
  75. if (seenValues.length > MAX_RECURSIVE_DEPTH) {
  76. return '[Array]';
  77. }
  78. const len = array.length;
  79. const items = [];
  80. for (let i = 0; i < len; ++i) {
  81. items.push(formatValue(array[i], seenValues));
  82. }
  83. return '[' + items.join(', ') + ']';
  84. }
  85. function getObjectTag(object) {
  86. const tag = Object.prototype.toString
  87. .call(object)
  88. .replace(/^\[object /, '')
  89. .replace(/]$/, '');
  90. if (tag === 'Object' && typeof object.constructor === 'function') {
  91. const name = object.constructor.name;
  92. if (typeof name === 'string' && name !== '') {
  93. return name;
  94. }
  95. }
  96. return tag;
  97. }