index.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. 'use strict';
  2. const hasGulplog = require('has-gulplog');
  3. const micromatch = require('micromatch');
  4. const unique = require('array-unique');
  5. const findup = require('findup-sync');
  6. const resolve = require('resolve');
  7. const path = require('path');
  8. function arrayify(el) {
  9. return Array.isArray(el) ? el : [el];
  10. }
  11. function camelize(str) {
  12. return str.replace(/-(\w)/g, (m, p1) => p1.toUpperCase());
  13. }
  14. // code from https://github.com/gulpjs/gulp-util/blob/master/lib/log.js
  15. // to use the same functionality as gulp-util for backwards compatibility
  16. // with gulp 3x cli
  17. function logger() {
  18. if (hasGulplog()) {
  19. // specifically deferring loading here to keep from registering it globally
  20. const gulplog = require('gulplog');
  21. gulplog.info.apply(gulplog, arguments);
  22. } else {
  23. // specifically deferring loading because it might not be used
  24. const fancylog = require('fancy-log');
  25. fancylog.apply(null, arguments);
  26. }
  27. }
  28. function getPattern(options) {
  29. const defaultPatterns = ['gulp-*', 'gulp.*', '@*/gulp{-,.}*'];
  30. const overridePattern = 'overridePattern' in options ? !!options.overridePattern : true;
  31. if (overridePattern) {
  32. return arrayify(options.pattern || defaultPatterns);
  33. }
  34. return defaultPatterns.concat(arrayify(options.pattern));
  35. }
  36. module.exports = function(options) {
  37. const finalObject = {};
  38. let requireFn;
  39. options = options || {};
  40. const DEBUG = options.DEBUG || false;
  41. const pattern = getPattern(options);
  42. const config = options.config || findup('package.json', { cwd: path.dirname(module.parent.filename) });
  43. const scope = arrayify(options.scope || ['dependencies', 'devDependencies', 'peerDependencies']);
  44. const replaceString = options.replaceString || /^gulp(-|\.)/;
  45. const camelizePluginName = options.camelize !== false;
  46. const lazy = 'lazy' in options ? !!options.lazy : true;
  47. const renameObj = options.rename || {};
  48. const maintainScope = 'maintainScope' in options ? !!options.maintainScope : true;
  49. logDebug(`Debug enabled with options: ${JSON.stringify(options)}`);
  50. const renameFn = options.renameFn || function(name) {
  51. name = name.replace(replaceString, '');
  52. return camelizePluginName ? camelize(name) : name;
  53. };
  54. const postRequireTransforms = options.postRequireTransforms || {};
  55. if (typeof options.requireFn === 'function') {
  56. requireFn = options.requireFn;
  57. } else if (typeof config === 'string') {
  58. requireFn = function(name) {
  59. // This searches up from the specified package.json file, making sure
  60. // the config option behaves as expected. See issue #56.
  61. const src = resolve.sync(name, { basedir: path.dirname(config) });
  62. return require(src);
  63. };
  64. } else {
  65. requireFn = require;
  66. }
  67. const configObject = (typeof config === 'string') ? require(config) : config;
  68. if (!configObject) {
  69. throw new Error('Could not find dependencies. Do you have a package.json file in your project?');
  70. }
  71. const names = scope.reduce((result, prop) => result.concat(Object.keys(configObject[prop] || {})), []);
  72. logDebug(`${names.length} plugin(s) found: ${names.join(' ')}`);
  73. pattern.push('!gulp-load-plugins');
  74. function logDebug(message) {
  75. if (DEBUG) {
  76. logger(`gulp-load-plugins: ${message}`);
  77. }
  78. }
  79. function defineProperty(object, transform, requireName, name, maintainScope) {
  80. let err;
  81. if (object[requireName]) {
  82. logDebug(`error: defineProperty ${name}`);
  83. err = maintainScope
  84. ? `Could not define the property "${requireName}", you may have repeated dependencies in your package.json like` + ` "gulp-${requireName}" and ` + `"${requireName}"`
  85. : `Could not define the property "${requireName}", you may have repeated a dependency in another scope like` + ` "gulp-${requireName}" and ` + `"@foo/gulp-${requireName}"`;
  86. throw new Error(err);
  87. }
  88. if (lazy) {
  89. logDebug(`lazyload: adding property ${requireName}`);
  90. Object.defineProperty(object, requireName, {
  91. enumerable: true,
  92. get: function() {
  93. logDebug(`lazyload: requiring ${name}...`);
  94. return transform(requireName, requireFn(name));
  95. }
  96. });
  97. } else {
  98. logDebug(`requiring ${name}...`);
  99. object[requireName] = transform(requireName, requireFn(name));
  100. }
  101. }
  102. function getRequireName(name) {
  103. let requireName;
  104. if (renameObj[name]) {
  105. requireName = options.rename[name];
  106. } else {
  107. requireName = renameFn(name);
  108. }
  109. logDebug(`renaming ${name} to ${requireName}`);
  110. return requireName;
  111. }
  112. function applyTransform(requireName, plugin) {
  113. const transform = postRequireTransforms[requireName];
  114. if (transform && typeof transform === 'function') { // if postRequireTransform function is passed, pass it the plugin and return it
  115. logDebug(`transforming ${requireName}`);
  116. return transform(plugin);
  117. } else {
  118. return plugin; // if no postRequireTransform function passed, return the plugin as is
  119. }
  120. }
  121. const scopeTest = /^@/;
  122. const scopeDecomposition = /^@(.+)\/(.+)/;
  123. unique(micromatch(names, pattern)).forEach((name) => {
  124. let decomposition;
  125. let fObject = finalObject;
  126. if (scopeTest.test(name)) {
  127. decomposition = scopeDecomposition.exec(name);
  128. if (maintainScope) {
  129. if (!Object.prototype.hasOwnProperty.call(fObject, decomposition[1])) {
  130. finalObject[decomposition[1]] = {};
  131. }
  132. fObject = finalObject[decomposition[1]];
  133. }
  134. defineProperty(fObject, applyTransform, getRequireName(decomposition[2]), name, maintainScope);
  135. } else {
  136. defineProperty(fObject, applyTransform, getRequireName(name), name, maintainScope);
  137. }
  138. });
  139. return finalObject;
  140. };
  141. // Necessary to get the current `module.parent` and resolve paths correctly.
  142. delete require.cache[__filename];