You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. 'use strict'
  2. module.exports = rfdc
  3. function copyBuffer (cur) {
  4. if (cur instanceof Buffer) {
  5. return Buffer.from(cur)
  6. }
  7. return new cur.constructor(cur.buffer.slice(), cur.byteOffset, cur.length)
  8. }
  9. function rfdc (opts) {
  10. opts = opts || {}
  11. if (opts.circles) return rfdcCircles(opts)
  12. const constructorHandlers = new Map()
  13. constructorHandlers.set(Date, (o) => new Date(o))
  14. constructorHandlers.set(Map, (o, fn) => new Map(cloneArray(Array.from(o), fn)))
  15. constructorHandlers.set(Set, (o, fn) => new Set(cloneArray(Array.from(o), fn)))
  16. if (opts.constructorHandlers) {
  17. for (const handler of opts.constructorHandlers) {
  18. constructorHandlers.set(handler[0], handler[1])
  19. }
  20. }
  21. let handler = null
  22. return opts.proto ? cloneProto : clone
  23. function cloneArray (a, fn) {
  24. const keys = Object.keys(a)
  25. const a2 = new Array(keys.length)
  26. for (let i = 0; i < keys.length; i++) {
  27. const k = keys[i]
  28. const cur = a[k]
  29. if (typeof cur !== 'object' || cur === null) {
  30. a2[k] = cur
  31. } else if (cur.constructor !== Object && (handler = constructorHandlers.get(cur.constructor))) {
  32. a2[k] = handler(cur, fn)
  33. } else if (ArrayBuffer.isView(cur)) {
  34. a2[k] = copyBuffer(cur)
  35. } else {
  36. a2[k] = fn(cur)
  37. }
  38. }
  39. return a2
  40. }
  41. function clone (o) {
  42. if (typeof o !== 'object' || o === null) return o
  43. if (Array.isArray(o)) return cloneArray(o, clone)
  44. if (o.constructor !== Object && (handler = constructorHandlers.get(o.constructor))) {
  45. return handler(o, clone)
  46. }
  47. const o2 = {}
  48. for (const k in o) {
  49. if (Object.hasOwnProperty.call(o, k) === false) continue
  50. const cur = o[k]
  51. if (typeof cur !== 'object' || cur === null) {
  52. o2[k] = cur
  53. } else if (cur.constructor !== Object && (handler = constructorHandlers.get(cur.constructor))) {
  54. o2[k] = handler(cur, clone)
  55. } else if (ArrayBuffer.isView(cur)) {
  56. o2[k] = copyBuffer(cur)
  57. } else {
  58. o2[k] = clone(cur)
  59. }
  60. }
  61. return o2
  62. }
  63. function cloneProto (o) {
  64. if (typeof o !== 'object' || o === null) return o
  65. if (Array.isArray(o)) return cloneArray(o, cloneProto)
  66. if (o.constructor !== Object && (handler = constructorHandlers.get(o.constructor))) {
  67. return handler(o, cloneProto)
  68. }
  69. const o2 = {}
  70. for (const k in o) {
  71. const cur = o[k]
  72. if (typeof cur !== 'object' || cur === null) {
  73. o2[k] = cur
  74. } else if (cur.constructor !== Object && (handler = constructorHandlers.get(cur.constructor))) {
  75. o2[k] = handler(cur, cloneProto)
  76. } else if (ArrayBuffer.isView(cur)) {
  77. o2[k] = copyBuffer(cur)
  78. } else {
  79. o2[k] = cloneProto(cur)
  80. }
  81. }
  82. return o2
  83. }
  84. }
  85. function rfdcCircles (opts) {
  86. const refs = []
  87. const refsNew = []
  88. const constructorHandlers = new Map()
  89. constructorHandlers.set(Date, (o) => new Date(o))
  90. constructorHandlers.set(Map, (o, fn) => new Map(cloneArray(Array.from(o), fn)))
  91. constructorHandlers.set(Set, (o, fn) => new Set(cloneArray(Array.from(o), fn)))
  92. if (opts.constructorHandlers) {
  93. for (const handler of opts.constructorHandlers) {
  94. constructorHandlers.set(handler[0], handler[1])
  95. }
  96. }
  97. let handler = null
  98. return opts.proto ? cloneProto : clone
  99. function cloneArray (a, fn) {
  100. const keys = Object.keys(a)
  101. const a2 = new Array(keys.length)
  102. for (let i = 0; i < keys.length; i++) {
  103. const k = keys[i]
  104. const cur = a[k]
  105. if (typeof cur !== 'object' || cur === null) {
  106. a2[k] = cur
  107. } else if (cur.constructor !== Object && (handler = constructorHandlers.get(cur.constructor))) {
  108. a2[k] = handler(cur, fn)
  109. } else if (ArrayBuffer.isView(cur)) {
  110. a2[k] = copyBuffer(cur)
  111. } else {
  112. const index = refs.indexOf(cur)
  113. if (index !== -1) {
  114. a2[k] = refsNew[index]
  115. } else {
  116. a2[k] = fn(cur)
  117. }
  118. }
  119. }
  120. return a2
  121. }
  122. function clone (o) {
  123. if (typeof o !== 'object' || o === null) return o
  124. if (Array.isArray(o)) return cloneArray(o, clone)
  125. if (o.constructor !== Object && (handler = constructorHandlers.get(o.constructor))) {
  126. return handler(o, clone)
  127. }
  128. const o2 = {}
  129. refs.push(o)
  130. refsNew.push(o2)
  131. for (const k in o) {
  132. if (Object.hasOwnProperty.call(o, k) === false) continue
  133. const cur = o[k]
  134. if (typeof cur !== 'object' || cur === null) {
  135. o2[k] = cur
  136. } else if (cur.constructor !== Object && (handler = constructorHandlers.get(cur.constructor))) {
  137. o2[k] = handler(cur, clone)
  138. } else if (ArrayBuffer.isView(cur)) {
  139. o2[k] = copyBuffer(cur)
  140. } else {
  141. const i = refs.indexOf(cur)
  142. if (i !== -1) {
  143. o2[k] = refsNew[i]
  144. } else {
  145. o2[k] = clone(cur)
  146. }
  147. }
  148. }
  149. refs.pop()
  150. refsNew.pop()
  151. return o2
  152. }
  153. function cloneProto (o) {
  154. if (typeof o !== 'object' || o === null) return o
  155. if (Array.isArray(o)) return cloneArray(o, cloneProto)
  156. if (o.constructor !== Object && (handler = constructorHandlers.get(o.constructor))) {
  157. return handler(o, cloneProto)
  158. }
  159. const o2 = {}
  160. refs.push(o)
  161. refsNew.push(o2)
  162. for (const k in o) {
  163. const cur = o[k]
  164. if (typeof cur !== 'object' || cur === null) {
  165. o2[k] = cur
  166. } else if (cur.constructor !== Object && (handler = constructorHandlers.get(cur.constructor))) {
  167. o2[k] = handler(cur, cloneProto)
  168. } else if (ArrayBuffer.isView(cur)) {
  169. o2[k] = copyBuffer(cur)
  170. } else {
  171. const i = refs.indexOf(cur)
  172. if (i !== -1) {
  173. o2[k] = refsNew[i]
  174. } else {
  175. o2[k] = cloneProto(cur)
  176. }
  177. }
  178. }
  179. refs.pop()
  180. refsNew.pop()
  181. return o2
  182. }
  183. }