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