UNPKG

funstream

Version:

Funstream gives you iteratorish methods on your streams.

209 lines (198 loc) 7.32 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 FlatMapStream let ReduceStream let ForEachStream const OPTS = Symbol('opts') const ISFUN = Symbol('isFun') const PROMISES = Symbol('promises') const RESULT = Symbol('result') class FunStream { init (opts) { this[OPTS] = Object.assign({Promise: Promise}, opts || {}) this[ISFUN] = true this[PROMISES] = {} this[RESULT] = null } 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])) }) } 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])) }) } 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) }) } 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) } 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) } 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) resolve(acc) }) } } 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)) } forEach (forEachWith, forEachOpts) { if (!ForEachStream) ForEachStream = require('./for-each-stream.js') const opts = Object.assign({}, this[OPTS], forEachOpts || {}) return this.pipe(ForEachStream(forEachWith, opts)) } } 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 P = (opts && opts.Promise) || fun.Promise const cls = typeof stream === 'function' ? stream : null !cls && mixinPromiseStream(P, stream) 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.finished) obj.finished = FunStream.prototype.finished if (!cls || !obj.closed) obj.closed = FunStream.prototype.closed } if (is.Readable(obj)) { if (!cls || !obj.ended) obj.ended = FunStream.prototype.ended } if (!cls || !obj.filter) obj.filter = FunStream.prototype.filter if (!cls || !obj.map) obj.map = FunStream.prototype.map 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.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 const originalPipe = obj.pipe obj.pipe = function (into, opts) { this.on('error', err => { if (err.src === undefined) err.src = this into.emit('error', err) }) return fun(originalPipe.call(this, into, opts), this[OPTS]) } return obj } module.exports = FunStream