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 }