index.js
  1  /*!
  2   * use <https://github.com/jonschlinkert/use>
  3   *
  4   * Copyright (c) 2015-2017, Jon Schlinkert.
  5   * Released under the MIT License.
  6   */
  7  
  8  'use strict';
  9  
 10  module.exports = function base(app, options) {
 11    if (!isObject(app) && typeof app !== 'function') {
 12      throw new TypeError('expected an object or function');
 13    }
 14  
 15    var opts = isObject(options) ? options : {};
 16    var prop = typeof opts.prop === 'string' ? opts.prop : 'fns';
 17    if (!Array.isArray(app[prop])) {
 18      define(app, prop, []);
 19    }
 20  
 21    /**
 22     * Define a plugin function to be passed to use. The only
 23     * parameter exposed to the plugin is `app`, the object or function.
 24     * passed to `use(app)`. `app` is also exposed as `this` in plugins.
 25     *
 26     * Additionally, **if a plugin returns a function, the function will
 27     * be pushed onto the `fns` array**, allowing the plugin to be
 28     * called at a later point by the `run` method.
 29     *
 30     * ```js
 31     * var use = require('use');
 32     *
 33     * // define a plugin
 34     * function foo(app) {
 35     *   // do stuff
 36     * }
 37     *
 38     * var app = function(){};
 39     * use(app);
 40     *
 41     * // register plugins
 42     * app.use(foo);
 43     * app.use(bar);
 44     * app.use(baz);
 45     * ```
 46     * @name .use
 47     * @param {Function} `fn` plugin function to call
 48     * @api public
 49     */
 50  
 51    define(app, 'use', use);
 52  
 53    /**
 54     * Run all plugins on `fns`. Any plugin that returns a function
 55     * when called by `use` is pushed onto the `fns` array.
 56     *
 57     * ```js
 58     * var config = {};
 59     * app.run(config);
 60     * ```
 61     * @name .run
 62     * @param {Object} `value` Object to be modified by plugins.
 63     * @return {Object} Returns the object passed to `run`
 64     * @api public
 65     */
 66  
 67    define(app, 'run', function(val) {
 68      if (!isObject(val)) return;
 69  
 70      if (!val.use || !val.run) {
 71        define(val, prop, val[prop] || []);
 72        define(val, 'use', use);
 73      }
 74  
 75      if (!val[prop] || val[prop].indexOf(base) === -1) {
 76        val.use(base);
 77      }
 78  
 79      var self = this || app;
 80      var fns = self[prop];
 81      var len = fns.length;
 82      var idx = -1;
 83  
 84      while (++idx < len) {
 85        val.use(fns[idx]);
 86      }
 87      return val;
 88    });
 89  
 90    /**
 91     * Call plugin `fn`. If a function is returned push it into the
 92     * `fns` array to be called by the `run` method.
 93     */
 94  
 95    function use(type, fn, options) {
 96      var offset = 1;
 97  
 98      if (typeof type === 'string' || Array.isArray(type)) {
 99        fn = wrap(type, fn);
100        offset++;
101      } else {
102        options = fn;
103        fn = type;
104      }
105  
106      if (typeof fn !== 'function') {
107        throw new TypeError('expected a function');
108      }
109  
110      var self = this || app;
111      var fns = self[prop];
112  
113      var args = [].slice.call(arguments, offset);
114      args.unshift(self);
115  
116      if (typeof opts.hook === 'function') {
117        opts.hook.apply(self, args);
118      }
119  
120      var val = fn.apply(self, args);
121      if (typeof val === 'function' && fns.indexOf(val) === -1) {
122        fns.push(val);
123      }
124      return self;
125    }
126  
127    /**
128     * Wrap a named plugin function so that it's only called on objects of the
129     * given `type`
130     *
131     * @param {String} `type`
132     * @param {Function} `fn` Plugin function
133     * @return {Function}
134     */
135  
136    function wrap(type, fn) {
137      return function plugin() {
138        return this.type === type ? fn.apply(this, arguments) : plugin;
139      };
140    }
141  
142    return app;
143  };
144  
145  function isObject(val) {
146    return val && typeof val === 'object' && !Array.isArray(val);
147  }
148  
149  function define(obj, key, val) {
150    Object.defineProperty(obj, key, {
151      configurable: true,
152      writable: true,
153      value: val
154    });
155  }