index.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. // put javascript in here
  2. 'use strict'
  3. const Parser = require('jsonparse')
  4. const Minipass = require('minipass')
  5. class JSONStreamError extends Error {
  6. constructor (err, caller) {
  7. super(err.message)
  8. Error.captureStackTrace(this, caller || this.constructor)
  9. }
  10. get name () {
  11. return 'JSONStreamError'
  12. }
  13. set name (n) {}
  14. }
  15. const check = (x, y) =>
  16. typeof x === 'string' ? String(y) === x
  17. : x && typeof x.test === 'function' ? x.test(y)
  18. : typeof x === 'boolean' || typeof x === 'object' ? x
  19. : typeof x === 'function' ? x(y)
  20. : false
  21. const _parser = Symbol('_parser')
  22. const _onValue = Symbol('_onValue')
  23. const _onTokenOriginal = Symbol('_onTokenOriginal')
  24. const _onToken = Symbol('_onToken')
  25. const _onError = Symbol('_onError')
  26. const _count = Symbol('_count')
  27. const _path = Symbol('_path')
  28. const _map = Symbol('_map')
  29. const _root = Symbol('_root')
  30. const _header = Symbol('_header')
  31. const _footer = Symbol('_footer')
  32. const _setHeaderFooter = Symbol('_setHeaderFooter')
  33. const _ending = Symbol('_ending')
  34. class JSONStream extends Minipass {
  35. constructor (opts = {}) {
  36. super({
  37. ...opts,
  38. objectMode: true,
  39. })
  40. this[_ending] = false
  41. const parser = this[_parser] = new Parser()
  42. parser.onValue = value => this[_onValue](value)
  43. this[_onTokenOriginal] = parser.onToken
  44. parser.onToken = (token, value) => this[_onToken](token, value)
  45. parser.onError = er => this[_onError](er)
  46. this[_count] = 0
  47. this[_path] = typeof opts.path === 'string'
  48. ? opts.path.split('.').map(e =>
  49. e === '$*' ? { emitKey: true }
  50. : e === '*' ? true
  51. : e === '' ? { recurse: true }
  52. : e)
  53. : Array.isArray(opts.path) && opts.path.length ? opts.path
  54. : null
  55. this[_map] = typeof opts.map === 'function' ? opts.map : null
  56. this[_root] = null
  57. this[_header] = null
  58. this[_footer] = null
  59. this[_count] = 0
  60. }
  61. [_setHeaderFooter] (key, value) {
  62. // header has not been emitted yet
  63. if (this[_header] !== false) {
  64. this[_header] = this[_header] || {}
  65. this[_header][key] = value
  66. }
  67. // footer has not been emitted yet but header has
  68. if (this[_footer] !== false && this[_header] === false) {
  69. this[_footer] = this[_footer] || {}
  70. this[_footer][key] = value
  71. }
  72. }
  73. [_onError] (er) {
  74. // error will always happen during a write() call.
  75. const caller = this[_ending] ? this.end : this.write
  76. this[_ending] = false
  77. return this.emit('error', new JSONStreamError(er, caller))
  78. }
  79. [_onToken] (token, value) {
  80. const parser = this[_parser]
  81. this[_onTokenOriginal].call(parser, token, value)
  82. if (parser.stack.length === 0) {
  83. if (this[_root]) {
  84. const root = this[_root]
  85. if (!this[_path])
  86. super.write(root)
  87. this[_root] = null
  88. this[_count] = 0
  89. }
  90. }
  91. }
  92. [_onValue] (value) {
  93. const parser = this[_parser]
  94. // the LAST onValue encountered is the root object.
  95. // just overwrite it each time.
  96. this[_root] = value
  97. if(!this[_path]) return
  98. let i = 0 // iterates on path
  99. let j = 0 // iterates on stack
  100. let emitKey = false
  101. let emitPath = false
  102. while (i < this[_path].length) {
  103. const key = this[_path][i]
  104. j++
  105. if (key && !key.recurse) {
  106. const c = (j === parser.stack.length) ? parser : parser.stack[j]
  107. if (!c) return
  108. if (!check(key, c.key)) {
  109. this[_setHeaderFooter](c.key, value)
  110. return
  111. }
  112. emitKey = !!key.emitKey;
  113. emitPath = !!key.emitPath;
  114. i++
  115. } else {
  116. i++
  117. if (i >= this[_path].length)
  118. return
  119. const nextKey = this[_path][i]
  120. if (!nextKey)
  121. return
  122. while (true) {
  123. const c = (j === parser.stack.length) ? parser : parser.stack[j]
  124. if (!c) return
  125. if (check(nextKey, c.key)) {
  126. i++
  127. if (!Object.isFrozen(parser.stack[j]))
  128. parser.stack[j].value = null
  129. break
  130. } else {
  131. this[_setHeaderFooter](c.key, value)
  132. }
  133. j++
  134. }
  135. }
  136. }
  137. // emit header
  138. if (this[_header]) {
  139. const header = this[_header]
  140. this[_header] = false
  141. this.emit('header', header)
  142. }
  143. if (j !== parser.stack.length) return
  144. this[_count] ++
  145. const actualPath = parser.stack.slice(1)
  146. .map(e => e.key).concat([parser.key])
  147. if (value !== null && value !== undefined) {
  148. const data = this[_map] ? this[_map](value, actualPath) : value
  149. if (data !== null && data !== undefined) {
  150. const emit = emitKey || emitPath ? { value: data } : data
  151. if (emitKey)
  152. emit.key = parser.key
  153. if (emitPath)
  154. emit.path = actualPath
  155. super.write(emit)
  156. }
  157. }
  158. if (parser.value)
  159. delete parser.value[parser.key]
  160. for (const k of parser.stack) {
  161. k.value = null
  162. }
  163. }
  164. write (chunk, encoding, cb) {
  165. if (typeof encoding === 'function')
  166. cb = encoding, encoding = null
  167. if (typeof chunk === 'string')
  168. chunk = Buffer.from(chunk, encoding)
  169. else if (!Buffer.isBuffer(chunk))
  170. return this.emit('error', new TypeError(
  171. 'Can only parse JSON from string or buffer input'))
  172. this[_parser].write(chunk)
  173. if (cb)
  174. cb()
  175. return this.flowing
  176. }
  177. end (chunk, encoding, cb) {
  178. this[_ending] = true
  179. if (typeof encoding === 'function')
  180. cb = encoding, encoding = null
  181. if (typeof chunk === 'function')
  182. cb = chunk, chunk = null
  183. if (chunk)
  184. this.write(chunk, encoding)
  185. if (cb)
  186. this.once('end', cb)
  187. const h = this[_header]
  188. this[_header] = null
  189. const f = this[_footer]
  190. this[_footer] = null
  191. if (h)
  192. this.emit('header', h)
  193. if (f)
  194. this.emit('footer', f)
  195. return super.end()
  196. }
  197. static get JSONStreamError () { return JSONStreamError }
  198. static parse (path, map) {
  199. return new JSONStream({path, map})
  200. }
  201. }
  202. module.exports = JSONStream