hybridables/always-done

View on GitHub
index.js

Summary

Maintainability
B
4 hrs
Test Coverage
/*!
 * always-done <https://github.com/hybridables/always-done>
 *
 * Copyright (c) 2015-2016 Charlike Mike Reagent <@tunnckoCore> (http://www.tunnckocore.tk)
 * Released under the MIT license.
 */

'use strict'

var utils = require('./utils')

/**
 * > Handle completion of `fn` and optionally
 * pass `done` callback, otherwise it returns a thunk.
 * If that `thunk` does not accept function, it returns
 * another thunk until you pass `done` to it.
 *
 * **Example**
 *
 * ```js
 * var alwaysDone = require('always-done')
 * var options = {
 *   context: { num: 123, bool: true }
 *   args: [require('assert')]
 * }
 *
 * alwaysDone(function (assert, next) {
 *   assert.strictEqual(this.num, 123)
 *   assert.strictEqual(this.bool, true)
 *   next()
 * }, options, function (err) {
 *   console.log(err, 'done') // => null, 'done'
 * })
 *
 * alwaysDone(function (cb) {
 *   cb(new Error('foo bar'))
 * }, function done (err) {
 *   console.log(err) // => Error: foo bar
 * })
 *
 * ```
 *
 * @param  {Function} `<fn>` function to be called
 * @param  {Object} `[opts]` optional options, such as `context` and `args`, passed to [try-catch-core][]
 * @param  {Object} `[opts.context]` context to be passed to `fn`
 * @param  {Array} `[opts.args]` custom argument(s) to be pass to `fn`, given value is arrayified
 * @param  {Boolean} `[opts.passCallback]` pass `true` if you want `cb` to be passed to `fn` args
 * @param  {Function} `[done]` on completion
 * @return {Function} thunk until you pass `done` to that thunk
 * @throws {TypError} if `fn` not a function.
 * @throws {TypError} if no function is passed to `thunk`.
 * @api public
 */

module.exports = function alwaysDone (fn, opts, done) {
  if (typeof opts === 'function') {
    done = opts
    opts = null
  }
  if (typeof done === 'function') {
    utils.tryCatchCore.call(this, fn, opts, function cb (err, val) {
      // handle errors
      if (err) {
        done(err)
        return
      }

      // handle returned errors
      if (utils.isTypeofError(val)) {
        done(val)
        return
      }

      // handle promises
      if (utils.isPromise(val)) {
        val.then(function (res) {
          done(null, res)
        }, done)
        return
      }

      // handle observables
      if (val && typeof val.subscribe === 'function') {
        utils.subscribe(val, done)
        return
      }

      // handle streams
      if (utils.isNodeStream(val) || utils.isChildProcess(val)) {
        utils.handleStreams(val, done)
        return
      }

      // handle sync
      done(null, val)
    })
    return
  }

  return function thunk (cb) {
    return alwaysDone.call(this, fn, opts, cb)
  }
}