db00bd86a149e3f41577a61db9415e4f007491e743d5f27077666c6db70adf78d36847fde62d9d5ce558aebb792d03ea219a17dcaa9a53b308ab490d3de24b 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. 'use strict'
  2. const check = require('check-types')
  3. const events = require('./events')
  4. const promise = require('./promise')
  5. const walk = require('./walk')
  6. module.exports = parse
  7. const NDJSON_STATE = new Map()
  8. /**
  9. * Public function `parse`.
  10. *
  11. * Returns a promise and asynchronously parses a stream of JSON data. If
  12. * there are no errors, the promise is resolved with the parsed data. If
  13. * errors occur, the promise is rejected with the first error.
  14. *
  15. * @param stream: Readable instance representing the incoming JSON.
  16. *
  17. * @option reviver: Transformation function, invoked depth-first.
  18. *
  19. * @option yieldRate: The number of data items to process per timeslice,
  20. * default is 16384.
  21. *
  22. * @option Promise: The promise constructor to use, defaults to bluebird.
  23. *
  24. * @option ndjson: Set this to true to parse newline-delimited JSON. In
  25. * this case, each call will be resolved with one value
  26. * from the stream. To parse the entire stream, calls
  27. * should be made sequentially one-at-a-time until the
  28. * returned promise resolves to `undefined`.
  29. **/
  30. function parse (stream, options = {}) {
  31. const Promise = promise(options)
  32. try {
  33. check.assert.maybe.function(options.reviver, 'Invalid reviver option')
  34. } catch (err) {
  35. return Promise.reject(err)
  36. }
  37. const errors = []
  38. const scopes = []
  39. const reviver = options.reviver
  40. const shouldHandleNdjson = !! options.ndjson
  41. let emitter, resolve, reject, scopeKey
  42. if (shouldHandleNdjson && NDJSON_STATE.has(stream)) {
  43. const state = NDJSON_STATE.get(stream)
  44. NDJSON_STATE.delete(stream)
  45. emitter = state.emitter
  46. setImmediate(state.resume)
  47. } else {
  48. emitter = walk(stream, options)
  49. }
  50. emitter.on(events.array, array)
  51. emitter.on(events.object, object)
  52. emitter.on(events.property, property)
  53. emitter.on(events.string, value)
  54. emitter.on(events.number, value)
  55. emitter.on(events.literal, value)
  56. emitter.on(events.endArray, endScope)
  57. emitter.on(events.endObject, endScope)
  58. emitter.on(events.end, end)
  59. emitter.on(events.error, error)
  60. emitter.on(events.dataError, error)
  61. if (shouldHandleNdjson) {
  62. emitter.on(events.endLine, endLine)
  63. }
  64. return new Promise((res, rej) => {
  65. resolve = res
  66. reject = rej
  67. })
  68. function array () {
  69. if (errors.length > 0) {
  70. return
  71. }
  72. beginScope([])
  73. }
  74. function beginScope (parsed) {
  75. if (errors.length > 0) {
  76. return
  77. }
  78. if (scopes.length > 0) {
  79. value(parsed)
  80. }
  81. scopes.push(parsed)
  82. }
  83. function value (v) {
  84. if (errors.length > 0) {
  85. return
  86. }
  87. if (scopes.length === 0) {
  88. return scopes.push(v)
  89. }
  90. const scope = scopes[scopes.length - 1]
  91. if (scopeKey) {
  92. scope[scopeKey] = v
  93. scopeKey = null
  94. } else {
  95. scope.push(v)
  96. }
  97. }
  98. function object () {
  99. if (errors.length > 0) {
  100. return
  101. }
  102. beginScope({})
  103. }
  104. function property (name) {
  105. if (errors.length > 0) {
  106. return
  107. }
  108. scopeKey = name
  109. }
  110. function endScope () {
  111. if (errors.length > 0) {
  112. return
  113. }
  114. if (scopes.length > 1) {
  115. scopes.pop()
  116. }
  117. }
  118. function end () {
  119. if (shouldHandleNdjson) {
  120. const resume = emitter.pause()
  121. emitter.removeAllListeners()
  122. NDJSON_STATE.set(stream, { emitter, resume })
  123. }
  124. if (errors.length > 0) {
  125. return reject(errors[0])
  126. }
  127. if (reviver) {
  128. scopes[0] = transform(scopes[0], '')
  129. }
  130. resolve(scopes[0])
  131. }
  132. function transform (obj, key) {
  133. if (obj && typeof obj === 'object') {
  134. Object.keys(obj).forEach(childKey => {
  135. obj[childKey] = transform(obj[childKey], childKey)
  136. })
  137. }
  138. return reviver(key, obj)
  139. }
  140. function error (e) {
  141. errors.push(e)
  142. }
  143. function endLine () {
  144. if (scopes.length > 0) {
  145. end()
  146. }
  147. }
  148. }