Skip to content

Commit 051d969

Browse files
committed
feat(json.stream): add utility function for streamed JSON parsing
1 parent f86e9d9 commit 051d969

File tree

5 files changed

+87
-12
lines changed

5 files changed

+87
-12
lines changed

README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,27 @@ const res = await fetch.json('/-/ping')
8282
console.log(res) // Body parsed as JSON
8383
```
8484

85+
#### <a name="fetch-json-stream"></a> `> fetch.json.stream(url, jsonPath, [opts]) -> Stream`
86+
87+
Performs a request to a given registry URL and parses the body of the response
88+
as JSON, with each entry being emitted through the stream.
89+
90+
The `jsonPath` argument is a [`JSONStream.parse()`
91+
path](https://github.com/dominictarr/JSONStream#jsonstreamparsepath), and the
92+
returned stream (unlike default `JSONStream`s), has a valid
93+
`Symbol.asyncIterator` implementation.
94+
95+
For available options, please see the section on [`fetch` options](#fetch-opts).
96+
97+
##### Example
98+
99+
```javascript
100+
console.log('https://npm.im/~zkat has access to the following packages:')
101+
for await (let {key, value} of fetch.json.stream('/-/user/zkat/package', '$*')) {
102+
console.log(`https://npm.im/${key} (perms: ${value})`)
103+
}
104+
```
105+
85106
#### <a name="fetch-opts"></a> `fetch` Options
86107

87108
Fetch options are optional, and can be passed in as either a Map-like object

index.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ const checkResponse = require('./check-response.js')
66
const config = require('./config.js')
77
const getAuth = require('./auth.js')
88
const fetch = require('make-fetch-happen')
9+
const JSONStream = require('JSONStream')
910
const npa = require('npm-package-arg')
11+
const {PassThrough} = require('stream')
1012
const qs = require('querystring')
1113
const url = require('url')
1214
const zlib = require('zlib')
@@ -110,6 +112,18 @@ function fetchJSON (uri, opts) {
110112
return regFetch(uri, opts).then(res => res.json())
111113
}
112114

115+
module.exports.json.stream = fetchJSONStream
116+
function fetchJSONStream (uri, jsonPath, opts) {
117+
const parser = JSONStream.parse(jsonPath)
118+
const pt = parser.pipe(new PassThrough({objectMode: true}))
119+
parser.on('error', err => pt.emit('error', err))
120+
regFetch(uri, opts).then(res => {
121+
res.body.on('error', err => parser.emit('error', err))
122+
res.body.pipe(parser)
123+
}, err => pt.emit('error', err))
124+
return pt
125+
}
126+
113127
module.exports.pickRegistry = pickRegistry
114128
function pickRegistry (spec, opts) {
115129
spec = npa(spec)

package-lock.json

Lines changed: 32 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
},
3030
"license": "ISC",
3131
"dependencies": {
32+
"JSONStream": "^1.3.4",
3233
"bluebird": "^3.5.1",
3334
"figgy-pudding": "^3.4.1",
3435
"lru-cache": "^4.1.3",
@@ -37,6 +38,7 @@
3738
},
3839
"devDependencies": {
3940
"cacache": "^11.0.2",
41+
"get-stream": "^4.0.0",
4042
"mkdirp": "^0.5.1",
4143
"nock": "^9.4.3",
4244
"npmlog": "^4.1.2",

test/index.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
const Buffer = require('safe-buffer').Buffer
44

55
const config = require('../config.js')
6+
const getStream = require('get-stream')
67
const npmlog = require('npmlog')
78
const PassThrough = require('stream').PassThrough
89
const silentLog = require('../silentlog.js')
@@ -237,6 +238,23 @@ test('json()', t => {
237238
.then(json => t.deepEqual(json, {hello: 'world'}, 'got json body'))
238239
})
239240

241+
test('fetch.json.stream()', t => {
242+
tnock(t, OPTS.registry).get('/hello').reply(200, {
243+
a: 1,
244+
b: 2,
245+
c: 3
246+
})
247+
return getStream.array(
248+
fetch.json.stream('/hello', '$*', OPTS)
249+
).then(data => {
250+
t.deepEqual(data, [
251+
{key: 'a', value: 1},
252+
{key: 'b', value: 2},
253+
{key: 'c', value: 3}
254+
], 'got a streamed JSON body')
255+
})
256+
})
257+
240258
test('opts.ignoreBody', t => {
241259
tnock(t, OPTS.registry)
242260
.get('/hello')

0 commit comments

Comments
 (0)