UNPKG

funstream

Version:

Funstream gives you iteratorish methods on your streams.

264 lines (254 loc) 9.67 kB
'use strict' const fun = require('./index.js') const mixinPromiseStream = require('./mixin-promise-stream.js') const is = require('isa-stream') let FilterStream let MapStream let MutateStream let FlatMapStream let ReduceStream let ForEachStream let LineStream const OPTS = Symbol('opts') const ISFUN = Symbol('isFun') const PROMISES = Symbol('promises') const RESULT = Symbol('result') const PIPE = Symbol('pipe') class FunStream { init (opts) { this[OPTS] = Object.assign({Promise: Promise}, opts || {}) this[ISFUN] = true this[PROMISES] = {} this[RESULT] = null this.fun = { ended: () => this.fun$ended(), finished: () => this.fun$finished(), writable: () => this.fun$writable() } } fun$ended () { if (!is.Readable(this)) throw new TypeError('This stream is not a readable stream, it will not end. Try `.finished()` instead.') if (this[PROMISES].ended) return this[PROMISES].ended return this[PROMISES].ended = new this[OPTS].Promise((resolve, reject) => { this.once('error', reject) this.once('end', () => setImmediate(resolve, this[RESULT])) }) } fun$finished () { if (!is.Writable(this)) throw new TypeError('This stream is not a writable stream, it will not finish. Try `.ended()` instead.') if (this[PROMISES].finished) return this[PROMISES].finished return this[PROMISES].finished = new this[OPTS].Promise((resolve, reject) => { this.once('error', reject) this.once('finish', () => setImmediate(resolve, this[RESULT])) }) } fun$closed () { if (!is.Writable(this)) throw new TypeError('This stream is not a writable stream, it will not close. Try `.ended()` instead.') if (this[PROMISES].closed) return this[PROMISES].closed return this[PROMISES].closed = new this[OPTS].Promise((resolve, reject) => { this.once('error', reject) this.once('close', resolve) }) } fun$writable () { if (!is.Writable(this)) throw new TypeError("This stream is not a writable stream, so it can't be... writable.") if (this.writable) return Promise.resolve() return new Promise(resolve => { this.once('drain', resolve) }) } async (todo) { if (todo) { const value = this[OPTS].async this[OPTS].async = true const next = todo.call(this, this) next[OPTS].async = value return next } else { this[OPTS].async = true return this } } sync (todo) { if (todo) { const value = this[OPTS].async this[OPTS].async = false const next = todo.call(this, this) next[OPTS].async = value return next } else { this[OPTS].async = false return this } } filter (filterWith, opts) { if (!FilterStream) FilterStream = require('./filter-stream.js') const filter = FilterStream(filterWith, opts ? Object.assign(this[OPTS], opts) : this[OPTS]) return this.pipe(filter) } map (mapWith, opts) { if (!MapStream) MapStream = require('./map-stream.js') const map = MapStream(mapWith, opts ? Object.assign(this[OPTS], opts) : this[OPTS]) return this.pipe(map) } mutate (mutateWith, opts) { if (!MutateStream) MutateStream = require('./mutate-stream.js') const mutate = MutateStream(mutateWith, opts ? Object.assign(this[OPTS], opts) : this[OPTS]) return this.pipe(mutate) } flat (opts) { return this.sync(o => o.flatMap(v => v, opts)) } flatMap (mapWith, opts) { if (!FlatMapStream) FlatMapStream = require('./flat-map-stream.js') const map = FlatMapStream(mapWith, opts ? Object.assign(this[OPTS], opts) : this[OPTS]) return this.pipe(map) } lines (opts) { if (!LineStream) LineStream = require('./line-stream.js') const lines = new LineStream(opts) return this.pipe(lines) } head (maxoutput) { let seen = 0 return this.sync(o => o.filter(() => seen++ < maxoutput)) } reduce (reduceWith, initial, reduceOpts) { if (!ReduceStream) ReduceStream = require('./reduce-stream.js') const opts = Object.assign({}, this[OPTS], reduceOpts || {}) return this.pipe(ReduceStream(reduceWith, initial, opts)) } reduceTo (reduceWith, initial, reduceOpts) { const opts = Object.assign({}, this[OPTS], reduceOpts || {}) let reduceToWith if (isAsync(reduceWith, 2, opts)) { reduceToWith = (acc, value, cb) => { return new opts.Promise((resolve, reject) => { const result = reduceWith(acc, value, err => err ? reject(err) : resolve(acc)) if (result && result.then) result.then(() => resolve(acc), reject) }) } } else { /* eslint no-sequences:0 */ reduceToWith = (acc, value) => (reduceWith(acc, value), acc) } return this.reduce(reduceToWith, initial, opts) } reduceToObject (reduceWith, opts) { return this.reduceTo(reduceWith, {}, opts) } reduceToArray (reduceWith, opts) { return this.reduceTo(reduceWith, [], opts) } list (opts) { return this.sync(o => o.reduceToArray((acc, val) => acc.push(val), opts)) } grab (whenDone, opts) { return fun(this.list().then(v => whenDone(v))) } sort (sortWith, opts) { return this.grab(v => v.sort(sortWith)) } concat (opts) { return this.sync(o => o.reduce((acc, val) => acc + String(val), '', opts)) } ndjson (opts) { return this.fromNdjson(opts) } json (opts) { return this.fromJson(opts) } fromNdjson (opts) { return this.lines(opts).flatMap(_ => _ === '' ? [] : JSON.parse(_), opts) } fromJson (opts) { return this.concat().then(str => JSON.parse(str)) } toJson (opts) { return this.grab(_ => JSON.stringify(_), opts) } toNdjson (opts) { return this.map(_ => JSON.stringify(_) + '\n', opts) } forEach (forEachWith, forEachOpts) { if (!ForEachStream) ForEachStream = require('./for-each-stream.js') const opts = Object.assign({}, this[OPTS], forEachOpts || {}) return this.pipe(ForEachStream(forEachWith, opts)) } pipe (into, opts) { this.on('error', err => { if (err && err.src === undefined) err.src = this into.emit('error', err) }) const funified = fun(this[PIPE](into, opts), this[OPTS], opts && opts.what) return funified } } // collect (opts) is an alias of list FunStream.prototype.collect = FunStream.prototype.list FunStream.isFun = stream => Boolean(stream && stream[ISFUN]) FunStream.mixin = mixinFun FunStream.isAsync = isAsync FunStream.funInit = function () { const fn = this.init ? this.init : this.prototype && this.prototype.init ? this.prototype.init : FunStream.prototype.init return fn.apply(this, arguments) } FunStream.OPTS = OPTS function isAsync (fun, args, opts) { if (fun.constructor.name === 'AsyncFunction') return true if (opts && opts.async != null) return opts.async return fun.length > args } function mixinFun (stream, opts) { if (FunStream.isFun(stream)) return stream const cls = typeof stream === 'function' ? stream : null !cls && mixinPromiseStream(stream, Object.assign({Promise: fun.Promise}, opts || {})) const obj = cls ? cls.prototype : stream if (cls) { cls.isFun = FunStream.isFun cls.mixin = FunStream.mixin cls.isAsync = FunStream.isAsync cls.funInit = FunStream.funInit } else { FunStream.funInit.call(obj, opts) } if (is.Writable(obj)) { if (!cls || !obj.fun$writable) obj.fun$writable = FunStream.prototype.fun$writable if (!cls || !obj.fun$finished) obj.fun$finished = FunStream.prototype.fun$finished if (!cls || !obj.fun$closed) obj.fun$closed = FunStream.prototype.fun$closed } if (is.Readable(obj)) { if (!cls || !obj.fun$ended) obj.fun$ended = FunStream.prototype.fun$ended } if (!cls || !obj.filter) obj.filter = FunStream.prototype.filter if (!cls || !obj.map) obj.map = FunStream.prototype.map if (!cls || !obj.mutate) obj.mutate = FunStream.prototype.mutate if (!cls || !obj.flat) obj.flat = FunStream.prototype.flat if (!cls || !obj.flatMap) obj.flatMap = FunStream.prototype.flatMap if (!cls || !obj.head) obj.head = FunStream.prototype.head if (!cls || !obj.reduce) obj.reduce = FunStream.prototype.reduce if (!cls || !obj.reduceTo) obj.reduceTo = FunStream.prototype.reduceTo if (!cls || !obj.reduceToArray) obj.reduceToArray = FunStream.prototype.reduceToArray if (!cls || !obj.reduceToObject) obj.reduceToObject = FunStream.prototype.reduceToObject if (!cls || !obj.concat) obj.concat = FunStream.prototype.concat if (!cls || !obj.list) obj.list = FunStream.prototype.list if (!cls || !obj.lines) obj.lines = FunStream.prototype.lines if (!cls || !obj.json) obj.json = FunStream.prototype.json if (!cls || !obj.ndjson) obj.ndjson = FunStream.prototype.ndjson if (!cls || !obj.toJson) obj.toJson = FunStream.prototype.toJson if (!cls || !obj.toNdjson) obj.toNdjson = FunStream.prototype.toNdjson if (!cls || !obj.fromJson) obj.fromJson = FunStream.prototype.fromJson if (!cls || !obj.fromNdjson) obj.fromNdjson = FunStream.prototype.fromNdjson if (!cls || !obj.collect) obj.collect = FunStream.prototype.collect if (!cls || !obj.grab) obj.grab = FunStream.prototype.grab if (!cls || !obj.sort) obj.sort = FunStream.prototype.sort if (!cls || !obj.forEach) obj.forEach = FunStream.prototype.forEach if (!cls || !obj.sync) obj.sync = FunStream.prototype.sync if (!cls || !obj.async) obj.async = FunStream.prototype.async if (!cls || !obj.whenWritable) obj.whenWritable = FunStream.prototype.whenWritable obj[PIPE] = obj.pipe Object.defineProperty(obj, 'pipe', { value: FunStream.prototype.pipe, writable: true }) return obj } module.exports = FunStream