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