index.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. module.exports = stringify
  2. stringify.default = stringify
  3. stringify.stable = deterministicStringify
  4. stringify.stableStringify = deterministicStringify
  5. var arr = []
  6. var replacerStack = []
  7. // Regular stringify
  8. function stringify (obj, replacer, spacer) {
  9. decirc(obj, '', [], undefined)
  10. var res
  11. if (replacerStack.length === 0) {
  12. res = JSON.stringify(obj, replacer, spacer)
  13. } else {
  14. res = JSON.stringify(obj, replaceGetterValues(replacer), spacer)
  15. }
  16. while (arr.length !== 0) {
  17. var part = arr.pop()
  18. if (part.length === 4) {
  19. Object.defineProperty(part[0], part[1], part[3])
  20. } else {
  21. part[0][part[1]] = part[2]
  22. }
  23. }
  24. return res
  25. }
  26. function decirc (val, k, stack, parent) {
  27. var i
  28. if (typeof val === 'object' && val !== null) {
  29. for (i = 0; i < stack.length; i++) {
  30. if (stack[i] === val) {
  31. var propertyDescriptor = Object.getOwnPropertyDescriptor(parent, k)
  32. if (propertyDescriptor.get !== undefined) {
  33. if (propertyDescriptor.configurable) {
  34. Object.defineProperty(parent, k, { value: '[Circular]' })
  35. arr.push([parent, k, val, propertyDescriptor])
  36. } else {
  37. replacerStack.push([val, k])
  38. }
  39. } else {
  40. parent[k] = '[Circular]'
  41. arr.push([parent, k, val])
  42. }
  43. return
  44. }
  45. }
  46. stack.push(val)
  47. // Optimize for Arrays. Big arrays could kill the performance otherwise!
  48. if (Array.isArray(val)) {
  49. for (i = 0; i < val.length; i++) {
  50. decirc(val[i], i, stack, val)
  51. }
  52. } else {
  53. var keys = Object.keys(val)
  54. for (i = 0; i < keys.length; i++) {
  55. var key = keys[i]
  56. decirc(val[key], key, stack, val)
  57. }
  58. }
  59. stack.pop()
  60. }
  61. }
  62. // Stable-stringify
  63. function compareFunction (a, b) {
  64. if (a < b) {
  65. return -1
  66. }
  67. if (a > b) {
  68. return 1
  69. }
  70. return 0
  71. }
  72. function deterministicStringify (obj, replacer, spacer) {
  73. var tmp = deterministicDecirc(obj, '', [], undefined) || obj
  74. var res
  75. if (replacerStack.length === 0) {
  76. res = JSON.stringify(tmp, replacer, spacer)
  77. } else {
  78. res = JSON.stringify(tmp, replaceGetterValues(replacer), spacer)
  79. }
  80. while (arr.length !== 0) {
  81. var part = arr.pop()
  82. if (part.length === 4) {
  83. Object.defineProperty(part[0], part[1], part[3])
  84. } else {
  85. part[0][part[1]] = part[2]
  86. }
  87. }
  88. return res
  89. }
  90. function deterministicDecirc (val, k, stack, parent) {
  91. var i
  92. if (typeof val === 'object' && val !== null) {
  93. for (i = 0; i < stack.length; i++) {
  94. if (stack[i] === val) {
  95. var propertyDescriptor = Object.getOwnPropertyDescriptor(parent, k)
  96. if (propertyDescriptor.get !== undefined) {
  97. if (propertyDescriptor.configurable) {
  98. Object.defineProperty(parent, k, { value: '[Circular]' })
  99. arr.push([parent, k, val, propertyDescriptor])
  100. } else {
  101. replacerStack.push([val, k])
  102. }
  103. } else {
  104. parent[k] = '[Circular]'
  105. arr.push([parent, k, val])
  106. }
  107. return
  108. }
  109. }
  110. if (typeof val.toJSON === 'function') {
  111. return
  112. }
  113. stack.push(val)
  114. // Optimize for Arrays. Big arrays could kill the performance otherwise!
  115. if (Array.isArray(val)) {
  116. for (i = 0; i < val.length; i++) {
  117. deterministicDecirc(val[i], i, stack, val)
  118. }
  119. } else {
  120. // Create a temporary object in the required way
  121. var tmp = {}
  122. var keys = Object.keys(val).sort(compareFunction)
  123. for (i = 0; i < keys.length; i++) {
  124. var key = keys[i]
  125. deterministicDecirc(val[key], key, stack, val)
  126. tmp[key] = val[key]
  127. }
  128. if (parent !== undefined) {
  129. arr.push([parent, k, val])
  130. parent[k] = tmp
  131. } else {
  132. return tmp
  133. }
  134. }
  135. stack.pop()
  136. }
  137. }
  138. // wraps replacer function to handle values we couldn't replace
  139. // and mark them as [Circular]
  140. function replaceGetterValues (replacer) {
  141. replacer = replacer !== undefined ? replacer : function (k, v) { return v }
  142. return function (key, val) {
  143. if (replacerStack.length > 0) {
  144. for (var i = 0; i < replacerStack.length; i++) {
  145. var part = replacerStack[i]
  146. if (part[1] === key && part[0] === val) {
  147. val = '[Circular]'
  148. replacerStack.splice(i, 1)
  149. break
  150. }
  151. }
  152. }
  153. return replacer.call(this, key, val)
  154. }
  155. }