index.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. 'use strict'
  2. var Parser = require('jsonparse')
  3. , through = require('through')
  4. var bufferFrom = Buffer.from && Buffer.from !== Uint8Array.from
  5. /*
  6. the value of this.stack that creationix's jsonparse has is weird.
  7. it makes this code ugly, but his problem is way harder that mine,
  8. so i'll forgive him.
  9. */
  10. exports.parse = function (path, map) {
  11. var header, footer
  12. var parser = new Parser()
  13. var stream = through(function (chunk) {
  14. if('string' === typeof chunk)
  15. chunk = bufferFrom ? Buffer.from(chunk) : new Buffer(chunk)
  16. parser.write(chunk)
  17. },
  18. function (data) {
  19. if(data)
  20. stream.write(data)
  21. if (header)
  22. stream.emit('header', header)
  23. if (footer)
  24. stream.emit('footer', footer)
  25. stream.queue(null)
  26. })
  27. if('string' === typeof path)
  28. path = path.split('.').map(function (e) {
  29. if (e === '$*')
  30. return {emitKey: true}
  31. else if (e === '*')
  32. return true
  33. else if (e === '') // '..'.split('.') returns an empty string
  34. return {recurse: true}
  35. else
  36. return e
  37. })
  38. var count = 0, _key
  39. if(!path || !path.length)
  40. path = null
  41. parser.onValue = function (value) {
  42. if (!this.root)
  43. stream.root = value
  44. if(! path) return
  45. var i = 0 // iterates on path
  46. var j = 0 // iterates on stack
  47. var emitKey = false;
  48. var emitPath = false;
  49. while (i < path.length) {
  50. var key = path[i]
  51. var c
  52. j++
  53. if (key && !key.recurse) {
  54. c = (j === this.stack.length) ? this : this.stack[j]
  55. if (!c) return
  56. if (! check(key, c.key)) {
  57. setHeaderFooter(c.key, value)
  58. return
  59. }
  60. emitKey = !!key.emitKey;
  61. emitPath = !!key.emitPath;
  62. i++
  63. } else {
  64. i++
  65. var nextKey = path[i]
  66. if (! nextKey) return
  67. while (true) {
  68. c = (j === this.stack.length) ? this : this.stack[j]
  69. if (!c) return
  70. if (check(nextKey, c.key)) {
  71. i++;
  72. if (!Object.isFrozen(this.stack[j]))
  73. this.stack[j].value = null
  74. break
  75. } else {
  76. setHeaderFooter(c.key, value)
  77. }
  78. j++
  79. }
  80. }
  81. }
  82. // emit header
  83. if (header) {
  84. stream.emit('header', header);
  85. header = false;
  86. }
  87. if (j !== this.stack.length) return
  88. count ++
  89. var actualPath = this.stack.slice(1).map(function(element) { return element.key }).concat([this.key])
  90. var data = value
  91. if(null != data)
  92. if(null != (data = map ? map(data, actualPath) : data)) {
  93. if (emitKey || emitPath) {
  94. data = { value: data };
  95. if (emitKey)
  96. data["key"] = this.key;
  97. if (emitPath)
  98. data["path"] = actualPath;
  99. }
  100. stream.queue(data)
  101. }
  102. if (this.value) delete this.value[this.key]
  103. for(var k in this.stack)
  104. if (!Object.isFrozen(this.stack[k]))
  105. this.stack[k].value = null
  106. }
  107. parser._onToken = parser.onToken;
  108. parser.onToken = function (token, value) {
  109. parser._onToken(token, value);
  110. if (this.stack.length === 0) {
  111. if (stream.root) {
  112. if(!path)
  113. stream.queue(stream.root)
  114. count = 0;
  115. stream.root = null;
  116. }
  117. }
  118. }
  119. parser.onError = function (err) {
  120. if(err.message.indexOf("at position") > -1)
  121. err.message = "Invalid JSON (" + err.message + ")";
  122. stream.emit('error', err)
  123. }
  124. return stream
  125. function setHeaderFooter(key, value) {
  126. // header has not been emitted yet
  127. if (header !== false) {
  128. header = header || {}
  129. header[key] = value
  130. }
  131. // footer has not been emitted yet but header has
  132. if (footer !== false && header === false) {
  133. footer = footer || {}
  134. footer[key] = value
  135. }
  136. }
  137. }
  138. function check (x, y) {
  139. if ('string' === typeof x)
  140. return y == x
  141. else if (x && 'function' === typeof x.exec)
  142. return x.exec(y)
  143. else if ('boolean' === typeof x || 'object' === typeof x)
  144. return x
  145. else if ('function' === typeof x)
  146. return x(y)
  147. return false
  148. }
  149. exports.stringify = function (op, sep, cl, indent) {
  150. indent = indent || 0
  151. if (op === false){
  152. op = ''
  153. sep = '\n'
  154. cl = ''
  155. } else if (op == null) {
  156. op = '[\n'
  157. sep = '\n,\n'
  158. cl = '\n]\n'
  159. }
  160. //else, what ever you like
  161. var stream
  162. , first = true
  163. , anyData = false
  164. stream = through(function (data) {
  165. anyData = true
  166. try {
  167. var json = JSON.stringify(data, null, indent)
  168. } catch (err) {
  169. return stream.emit('error', err)
  170. }
  171. if(first) { first = false ; stream.queue(op + json)}
  172. else stream.queue(sep + json)
  173. },
  174. function (data) {
  175. if(!anyData)
  176. stream.queue(op)
  177. stream.queue(cl)
  178. stream.queue(null)
  179. })
  180. return stream
  181. }
  182. exports.stringifyObject = function (op, sep, cl, indent) {
  183. indent = indent || 0
  184. if (op === false){
  185. op = ''
  186. sep = '\n'
  187. cl = ''
  188. } else if (op == null) {
  189. op = '{\n'
  190. sep = '\n,\n'
  191. cl = '\n}\n'
  192. }
  193. //else, what ever you like
  194. var first = true
  195. var anyData = false
  196. var stream = through(function (data) {
  197. anyData = true
  198. var json = JSON.stringify(data[0]) + ':' + JSON.stringify(data[1], null, indent)
  199. if(first) { first = false ; this.queue(op + json)}
  200. else this.queue(sep + json)
  201. },
  202. function (data) {
  203. if(!anyData) this.queue(op)
  204. this.queue(cl)
  205. this.queue(null)
  206. })
  207. return stream
  208. }