helpers.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. const ansiColors = {
  2. bold: ['1', '22'],
  3. dim: ['2', '22'],
  4. italic: ['3', '23'],
  5. underline: ['4', '24'],
  6. // 5 & 6 are blinking
  7. inverse: ['7', '27'],
  8. hidden: ['8', '28'],
  9. strike: ['9', '29'],
  10. // 10-20 are fonts
  11. // 21-29 are resets for 1-9
  12. black: ['30', '39'],
  13. red: ['31', '39'],
  14. green: ['32', '39'],
  15. yellow: ['33', '39'],
  16. blue: ['34', '39'],
  17. magenta: ['35', '39'],
  18. cyan: ['36', '39'],
  19. white: ['37', '39'],
  20. brightblack: ['30;1', '39'],
  21. brightred: ['31;1', '39'],
  22. brightgreen: ['32;1', '39'],
  23. brightyellow: ['33;1', '39'],
  24. brightblue: ['34;1', '39'],
  25. brightmagenta: ['35;1', '39'],
  26. brightcyan: ['36;1', '39'],
  27. brightwhite: ['37;1', '39'],
  28. grey: ['90', '39'],
  29. }
  30. const styles = {
  31. special: 'cyan',
  32. number: 'yellow',
  33. bigint: 'yellow',
  34. boolean: 'yellow',
  35. undefined: 'grey',
  36. null: 'bold',
  37. string: 'green',
  38. symbol: 'green',
  39. date: 'magenta',
  40. regexp: 'red',
  41. }
  42. export const truncator = '…'
  43. function colorise(value, styleType) {
  44. const color = ansiColors[styles[styleType]] || ansiColors[styleType]
  45. if (!color) {
  46. return String(value)
  47. }
  48. return `\u001b[${color[0]}m${String(value)}\u001b[${color[1]}m`
  49. }
  50. export function normaliseOptions({
  51. showHidden = false,
  52. depth = 2,
  53. colors = false,
  54. customInspect = true,
  55. showProxy = false,
  56. maxArrayLength = Infinity,
  57. breakLength = Infinity,
  58. seen = [],
  59. // eslint-disable-next-line no-shadow
  60. truncate = Infinity,
  61. stylize = String,
  62. } = {}) {
  63. const options = {
  64. showHidden: Boolean(showHidden),
  65. depth: Number(depth),
  66. colors: Boolean(colors),
  67. customInspect: Boolean(customInspect),
  68. showProxy: Boolean(showProxy),
  69. maxArrayLength: Number(maxArrayLength),
  70. breakLength: Number(breakLength),
  71. truncate: Number(truncate),
  72. seen,
  73. stylize,
  74. }
  75. if (options.colors) {
  76. options.stylize = colorise
  77. }
  78. return options
  79. }
  80. export function truncate(string, length, tail = truncator) {
  81. string = String(string)
  82. const tailLength = tail.length
  83. const stringLength = string.length
  84. if (tailLength > length && stringLength > tailLength) {
  85. return tail
  86. }
  87. if (stringLength > length && stringLength > tailLength) {
  88. return `${string.slice(0, length - tailLength)}${tail}`
  89. }
  90. return string
  91. }
  92. // eslint-disable-next-line complexity
  93. export function inspectList(list, options, inspectItem, separator = ', ') {
  94. inspectItem = inspectItem || options.inspect
  95. const size = list.length
  96. if (size === 0) return ''
  97. const originalLength = options.truncate
  98. let output = ''
  99. let peek = ''
  100. let truncated = ''
  101. for (let i = 0; i < size; i += 1) {
  102. const last = i + 1 === list.length
  103. const secondToLast = i + 2 === list.length
  104. truncated = `${truncator}(${list.length - i})`
  105. const value = list[i]
  106. // If there is more than one remaining we need to account for a separator of `, `
  107. options.truncate = originalLength - output.length - (last ? 0 : separator.length)
  108. const string = peek || inspectItem(value, options) + (last ? '' : separator)
  109. const nextLength = output.length + string.length
  110. const truncatedLength = nextLength + truncated.length
  111. // If this is the last element, and adding it would
  112. // take us over length, but adding the truncator wouldn't - then break now
  113. if (last && nextLength > originalLength && output.length + truncated.length <= originalLength) {
  114. break
  115. }
  116. // If this isn't the last or second to last element to scan,
  117. // but the string is already over length then break here
  118. if (!last && !secondToLast && truncatedLength > originalLength) {
  119. break
  120. }
  121. // Peek at the next string to determine if we should
  122. // break early before adding this item to the output
  123. peek = last ? '' : inspectItem(list[i + 1], options) + (secondToLast ? '' : separator)
  124. // If we have one element left, but this element and
  125. // the next takes over length, the break early
  126. if (!last && secondToLast && truncatedLength > originalLength && nextLength + peek.length > originalLength) {
  127. break
  128. }
  129. output += string
  130. // If the next element takes us to length -
  131. // but there are more after that, then we should truncate now
  132. if (!last && !secondToLast && nextLength + peek.length >= originalLength) {
  133. truncated = `${truncator}(${list.length - i - 1})`
  134. break
  135. }
  136. truncated = ''
  137. }
  138. return `${output}${truncated}`
  139. }
  140. function quoteComplexKey(key) {
  141. if (key.match(/^[a-zA-Z_][a-zA-Z_0-9]*$/)) {
  142. return key
  143. }
  144. return JSON.stringify(key)
  145. .replace(/'/g, "\\'")
  146. .replace(/\\"/g, '"')
  147. .replace(/(^"|"$)/g, "'")
  148. }
  149. export function inspectProperty([key, value], options) {
  150. options.truncate -= 2
  151. if (typeof key === 'string') {
  152. key = quoteComplexKey(key)
  153. } else if (typeof key !== 'number') {
  154. key = `[${options.inspect(key, options)}]`
  155. }
  156. options.truncate -= key.length
  157. value = options.inspect(value, options)
  158. return `${key}: ${value}`
  159. }