Skip to content

Commit fc0bdaf

Browse files
authored
2.0 (#6)
* Drop support for node 0.10 because streams are broken. Fix transform streams for node 0.12 * Make cycling off by default. * Emit errors from Promises and Streams. Add path property. * Complete rewrite.
1 parent de561b1 commit fc0bdaf

20 files changed

+11215
-1460
lines changed

.babelrc.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
module.exports = {
2+
presets: [
3+
['@babel/preset-env', {
4+
forceAllTransforms: true,
5+
modules: 'cjs',
6+
debug: false,
7+
useBuiltIns: 'usage',
8+
}],
9+
],
10+
exclude: 'node_modules/**',
11+
plugins: [
12+
['@babel/plugin-transform-runtime', {
13+
helpers: false,
14+
polyfill: true,
15+
regenerator: false,
16+
}],
17+
],
18+
};

.eslintignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
dist
2+
coverage
3+
examples
4+
node_modules
5+
*.gitignore*
6+
test

.eslintrc.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
module.exports = {
2+
"extends": "eslint-config-airbnb",
3+
"rules": {
4+
// enable additional rules
5+
"indent": ["error", 2],
6+
"linebreak-style": ["error", "unix"],
7+
8+
"no-underscore-dangle": "off",
9+
"no-control-regex": "off",
10+
},
11+
};

.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,11 @@ jspm_packages
3535

3636
# Optional REPL history
3737
.node_repl_history
38+
39+
# Build artifacts
40+
dist
41+
42+
# Test folder, because tests are compiled
43+
test/*
44+
45+
*.gitignore*

.travis.yml

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,40 @@
1+
# these are executed in order. each must pass for the next to be run
2+
stages:
3+
- lint
4+
- test
5+
- deploy
6+
7+
# defaults
18
language: node_js
2-
node_js:
3-
- '6'
4-
- '8'
5-
- '10'
6-
sudo: false
7-
script:
8-
- "npm run coverage"
9-
after_script:
10-
- "test -e ./coverage/lcov.info && cat ./coverage/lcov.info | coveralls"
9+
node_js: '10'
10+
# disable automatic npm install
11+
install: true
12+
13+
jobs:
14+
include:
15+
- stage: lint
16+
script: bash .travis/script.sh install lint
17+
node_js: '10'
18+
19+
- &node
20+
stage: test
21+
script: bash .travis/script.sh install build test
22+
node_js: '8'
23+
24+
- <<: *node
25+
node_js: '6'
26+
27+
- <<: *node
28+
node_js: '4'
29+
30+
- <<: *node
31+
node_js: '0.12'
32+
33+
# coverage includes testing, thats why 10 is not in test stage
34+
- stage: deploy
35+
script: bash .travis/script.sh install build coverage deploy
36+
node_js: '10'
37+
38+
env:
39+
global:
40+
secure: KFTSqBjB5xgGTgc1aInX/qHXM7U51G1fIMO5eJygsL9I0VDyUV7XoO096fTWR1uv8VZMWARgOFrb+ZF7jRuRo2hCfN937vVeH+dVFULil5Nkx/y6gOapCzU67mU8Y6W5wJ4xTabi4u7DndUIN+G8ffVcW1FHD7yu2Oc0eclzH6GFxuW6HBW/FCbuuRGjnkS8gthI1DqX8TirZrGGBmTC/QwdulSjOs8xo2QEpUuYdnFHe93q2j6dztJ0i+WOau/U/IFgvJIxLqgvglVFGUfdCJxYAwealAuWDAlOvMihrF9Tkil7WasttFCkxowbfRbp5TG3twE17uGer09+Q/RzJhr7F7jxi3Oyv0AucSd73g2VQpkuTC9EMgPR5dD1gn1pGlwQOHog90yFzg17bfIUk7deyZulNz784sdazlh3h4qFIjC2MZ9k7xdxYdvybT5pqcAK/tZ1MOIQjFYRxcgc4pWfkygmxF9QrkLX/HqwOY9rUa9D1W7kIgVb24x54hxYl5/Y+S1Ss4fHYXg6o8hxkYk1VRok+GgVO3EveKiE/QO76ouE5E70xcEEahGKIoZK8pFpiz9unPb1ZHlNiAre0TWvR5oxLyNgcTLT2yrEAROFvzMVfk49BqSx/1pkk9VvCxrVeGLIq2stP8gcfcgJ5e12bIa9pvkJMMyaN1C3id8=

.travis/script.sh

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#!/bin/bash
2+
3+
# Save current version
4+
NODE_VERSION=$(node --version)
5+
6+
source $NVM_DIR/nvm.sh
7+
8+
install() {
9+
echo install:restore:NODE_VERSION=$NODE_VERSION
10+
nvm install 10
11+
12+
# install with node v10
13+
nvm use 10
14+
15+
# install deps
16+
npm ci
17+
18+
# Restore current version
19+
nvm use $NODE_VERSION
20+
}
21+
22+
build() {
23+
echo build:restore:NODE_VERSION=$NODE_VERSION
24+
25+
# Builds with node v10
26+
nvm use 10
27+
28+
# actual build
29+
npm run build
30+
31+
# Restore current version
32+
nvm use $NODE_VERSION
33+
}
34+
35+
test() {
36+
npm run test
37+
}
38+
39+
lint() {
40+
npm run lint
41+
}
42+
43+
coverage() {
44+
npm run coverage
45+
npm run coveralls
46+
}
47+
48+
deploy() {
49+
# copy files for publish
50+
cp package.json dist/package.json
51+
cp package-lock.json dist/package-lock.json
52+
cp README.md dist/README.md
53+
cp LICENSE dist/LICENSE
54+
55+
# move into publish directory
56+
cd dist
57+
58+
# Set the NPM access token we will use to publish.
59+
npm config set registry https://registry.npmjs.org/
60+
npm config set //registry.npmjs.org/:_authToken ${NPM_TOKEN}
61+
62+
# dry publish run for non master
63+
npm pack
64+
if [ "$TRAVIS_BRANCH" == "master" ]
65+
then
66+
npm publish $(ls json-stream-stringify-*.tgz)
67+
fi
68+
}
69+
70+
# Loop over arguments
71+
for var in "$@"
72+
do
73+
# Check if the function exists (bash specific)
74+
if declare -f "$var" > /dev/null
75+
then
76+
# call argument
77+
"$var"
78+
else
79+
# Show a helpful error
80+
echo "'$var' is not a known function name in docker_fn.sh" >&2
81+
exit 1
82+
fi
83+
done

.vscode/settings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"eslint.enable": true
3+
}

README.md

Lines changed: 92 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,120 @@
11
# JSON Stream Stringify
22
[![NPM version][npm-image]][npm-url]
33
[![NPM Downloads][downloads-image]][downloads-url]
4-
[![Dependency Status][dependency-image]][dependency-url]
54
[![Build Status][travis-image]][travis-url]
65
[![Coverage Status][coveralls-image]][coveralls-url]
76
[![License][license-image]](LICENSE)
8-
[![Gratipay][gratipay-image]][gratipay-url]
7+
[![Donate][donate-image]][donate-url]
98

109
JSON Stringify as a Readable Stream with rescursive resolving of any readable streams and Promises.
1110

11+
## Important and Breaking Changes in v2
12+
- Completely rewritten from scratch
13+
- 100% Code Coverage! 🎉
14+
- Space argument finally implemented! 🎉
15+
- ⚠️ Cycling is off by default
16+
- ⚠️ JsonStreamStringify is now a constructor; use ``new`` operator
17+
- Removed dependency on global JSON.stringify, Async/Await and Generators
18+
- JsonStreamStringify is now compiled with babel to target ES5 (polyfills needed)
19+
- Rejected promises and input stream errors are now handled and emitted as errors
20+
- Added cyclic structure detection to prevent infinite recursion
21+
1222
## Main Features
13-
- Promises are rescursively resolved and the result is piped through JSONStreamStreamify
14-
- Streams (ObjectMode) are piped through a transform which pipes the data through JSONStreamStreamify (enabling recursive resolving)
15-
- Streams (Non-ObjectMode) is stringified and piped
23+
- Promises are rescursively resolved and the result is piped through JsonStreamStringify
24+
- Streams (Object mode) are recursively read and output as arrays
25+
- Streams (Non-Object mode) are output as a single string
1626
- Output is streamed optimally with as small chunks as possible
17-
- Decycling using Douglas Crockfords Cycle algorithm
18-
- Great memory management with reference release post process (When a key and value has been processed the value is dereferenced)
19-
- Stream pressure handling
27+
- Cycling of cyclical structures and dags using [Douglas Crockfords cycle algorithm](https://github.com/douglascrockford/JSON-js)*
28+
- Great memory management with reference release after processing and WeakMap/Set reference handling
29+
- Optimal stream pressure handling
30+
- Tested and runs on ES5** and ES2015
31+
- Bundled as UMD and Module
32+
33+
\* Off by default since v2
34+
\** With polyfills
2035

2136
## Install
2237

2338
```bash
2439
npm install --save json-stream-stringify
40+
41+
# Optional if you need polyfills
42+
# Make sure to include these if you target NodeJS <=v6 or browsers
43+
npm install --save @babel/polyfill @babel/runtime
44+
```
45+
46+
## Usage
47+
Using Node v8 or later with ESM / Webpack / Browserify / Rollup
48+
```javascript
49+
// No Polyfills
50+
import JsonStreamStringify from 'JsonStreamStringify';
51+
```
52+
```javascript
53+
// Polyfilled; loads only needed polyfills from @babel/polyfill @babel/runtime
54+
import JsonStreamStringify from 'JsonStreamStringify/module.polyfill';
55+
```
56+
57+
Using Node >=8 / Other ES2015 environments
58+
```javascript
59+
const JsonStreamStringify = require('JsonStreamStringify');
60+
```
61+
62+
Using Node <=6 / Other ES5 environments
63+
```javascript
64+
var JsonStreamStringify = require('JsonStreamStringify/umd.polyfill');
2565
```
2666

67+
**Note:** This library is primarily written for LTS versions of NodeJS. Other environments are not tested.
68+
**Note on non-NodeJS usage:** This module depends on node streams library. Any Streams3 compatible implementation should work - as long as it exports a Readable class, with instances that looks like readable streams.
69+
**Note on Polyfills:** I have taken measures to minify global pollution of polyfills but this library **does not load polyfills by default** because the polyfills modify native object prototypes and it goes against the [W3C recommendations](https://www.w3.org/2001/tag/doc/polyfills/#advice-for-library-and-framework-authors).
70+
2771
## API
2872

29-
### JSONStreamStringify(value[, replacer[, spaces[, noDecycle]]])
30-
Convert value to JSON string. Returns a readable stream.
31-
- ``value`` Any data to convert to JSON.
32-
- ``replacer`` Optional ``Function(key, value)`` or ``Array``.
33-
As a function the returned value replaces the value associated with the key. [Details](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#The_replacer_parameter)
73+
### `new JsonStreamStringify(value[, replacer[, spaces[, cycle]]])`
74+
75+
Streaming conversion of ``value`` to JSON string.
76+
77+
**Parameters**
78+
- ``value`` ``Any``
79+
Data to convert to JSON.
80+
81+
- ``replacer`` Optional ``Function(key, value)`` or ``Array``
82+
As a function the returned value replaces the value associated with the key. [Details](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#The_replacer_parameter)
3483
As an array all other keys are filtered. [Details](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#Example_with_an_array)
35-
- ``spaces`` Optional ``String`` or ``Number`` **Not yet implemented**
36-
- ``noDecycle`` Optional ``Boolean`` Set to ``true`` to disable decycling.
84+
85+
- ``spaces`` Optional ``String`` or ``Number``
86+
A String or Number object that's used to insert white space into the output JSON string for readability purposes. If this is a Number, it indicates the number of space characters to use as white space. If this is a String, the string is used as white space. If this parameter is not recognized as a finite number or valid string, no white space is used.
87+
88+
- ``cycle`` Optional ``Boolean``
89+
``true`` enables cycling of cyclical structures and dags.
90+
To restore cyclical structures; use [Crockfords Retrocycle method](https://github.com/douglascrockford/JSON-js) on the parsed object (not included in this module).
91+
92+
**Returns**
93+
- ``JsonStreamStringify`` object that exposes a [Streams3 interface](https://nodejs.org/api/stream.html#stream_class_stream_readable).
94+
95+
### jsonStreamStringify#path
96+
97+
**Returns**
98+
- ``Array[String, Number]``
99+
Current path being serialized as an array of Strings (keys of objects) and Numbers (index into arrays).
100+
Can be transformed into an mpath with ``.join('.')``.
101+
Useful in conjunction with ``.on('error', ...)``, for figuring out what path may have caused the error.
37102

38103
## Example Usage
39104
```javascript
40-
const JSONStreamStringify = require('json-stream-stringify');
105+
const JsonStreamStringify = require('json-stream-stringify');
41106

42-
JSONStreamStringify({
43-
aPromise: Promise.resolve(Promise.resolve("text")), // Promise may resolve more promises and streams which will be consumed and resolved
44-
aStream: ReadableObjectStream({a:1}, 'str'), // Stream may write more streams and promises which will be consumed and resolved
107+
const jsonStream = new JsonStreamStringify({
108+
// Promises and Streams may resolve more promises and/or streams which will be consumed and processed into json output
109+
aPromise: Promise.resolve(Promise.resolve("text")),
110+
aStream: ReadableObjectStream({a:1}, 'str'),
45111
arr: [1, 2, Promise.resolve(3), Promise.resolve([4, 5]), ReadableStream('a', 'b', 'c')],
46112
date: new Date(2016, 0, 2)
47-
}).pipe(process.stdout);
48-
113+
});
114+
jsonStream.once('error', () => console.log('Error at path', jsonStream.stack.join('.')));
115+
jsonStream.pipe(process.stdout);
49116
```
50-
Output (each line represents a write from JSONStreamStringify)
117+
Output (each line represents a write from jsonStreamStringify)
51118
```
52119
{
53120
"aPromise":
@@ -88,23 +155,9 @@ c
88155

89156
## Practical Example with Express + Mongoose
90157
```javascript
91-
app.get('/api/users', (req, res, next) => JSONStreamStringify(Users.find().stream()).pipe(res));
158+
app.get('/api/users', (req, res, next) => new JsonStreamStringify(Users.find().stream()).pipe(res));
92159
```
93160

94-
## TODO
95-
- Space option
96-
97-
Feel free to contribute.
98-
99-
## Technical Notes
100-
Uses toJSON when available, and JSON.stringify to stringify everything but objects and arrays.
101-
Streams with ObjectMode=true are output as arrays while ObjectMode=false output as a concatinated string (each chunk is piped with transforms).
102-
103-
Circular structures are handled using a WeakMap based implementation of [Douglas Crockfords Decycle method](https://github.com/douglascrockford/JSON-js/blob/master/cycle.js). To restore circular structures; use Crockfords Retrocycle method on the parsed object.
104-
105-
## Requirements
106-
NodeJS >4.2.2
107-
108161
# License
109162
[MIT](LICENSE)
110163

@@ -114,12 +167,10 @@ Copyright (c) 2016 Faleij [faleij@gmail.com](mailto:faleij@gmail.com)
114167
[npm-url]: https://npmjs.org/package/json-stream-stringify
115168
[downloads-image]: https://img.shields.io/npm/dm/json-stream-stringify.svg
116169
[downloads-url]: https://npmjs.org/package/json-stream-stringify
117-
[dependency-image]: https://gemnasium.com/Faleij/json-stream-stringify.svg
118-
[dependency-url]: https://gemnasium.com/Faleij/json-stream-stringify
119170
[travis-image]: https://travis-ci.org/Faleij/json-stream-stringify.svg?branch=master
120171
[travis-url]: https://travis-ci.org/Faleij/json-stream-stringify
121172
[coveralls-image]: https://coveralls.io/repos/Faleij/json-stream-stringify/badge.svg?branch=master&service=github
122173
[coveralls-url]: https://coveralls.io/github/Faleij/json-stream-stringify?branch=master
123174
[license-image]: https://img.shields.io/badge/license-MIT-blue.svg
124-
[gratipay-image]: https://img.shields.io/gratipay/faleij.svg
125-
[gratipay-url]: https://gratipay.com/faleij/
175+
[donate-image]: https://img.shields.io/badge/Donate-PayPal-green.svg
176+
[donate-url]: https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=faleij%40gmail%2ecom&lc=GB&item_name=faleij&item_number=jsonStreamStringify&currency_code=SEK&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted

0 commit comments

Comments
 (0)