'use strict'; var hasGulplog = require('has-gulplog'); var micromatch = require('micromatch'); var unique = require('array-unique'); var findup = require('findup-sync'); var resolve = require('resolve'); var path = require('path'); function arrayify(el) { return Array.isArray(el) ? el : [el]; } function camelize(str) { return str.replace(/-(\w)/g, function(m, p1) { return p1.toUpperCase(); }); } // code from https://github.com/gulpjs/gulp-util/blob/master/lib/log.js // to use the same functionality as gulp-util for backwards compatibility // with gulp 3x cli function logger() { if (hasGulplog()) { // specifically deferring loading here to keep from registering it globally var gulplog = require('gulplog'); gulplog.info.apply(gulplog, arguments); } else { // specifically defering loading because it might not be used var fancylog = require('fancy-log'); fancylog.apply(null, arguments); } } function getPattern(options) { var defaultPatterns = ['gulp-*', 'gulp.*', '@*/gulp{-,.}*']; var overridePattern = 'overridePattern' in options ? !!options.overridePattern : true; if (overridePattern) { return arrayify(options.pattern || defaultPatterns); } return defaultPatterns.concat(arrayify(options.pattern)); } module.exports = function(options) { var finalObject = {}; var configObject; var requireFn; options = options || {}; var DEBUG = options.DEBUG || false; var pattern = getPattern(options); var config = options.config || findup('package.json', { cwd: parentDir }); var scope = arrayify(options.scope || ['dependencies', 'devDependencies', 'peerDependencies']); var replaceString = options.replaceString || /^gulp(-|\.)/; var camelizePluginName = options.camelize !== false; var lazy = 'lazy' in options ? !!options.lazy : true; var renameObj = options.rename || {}; var maintainScope = 'maintainScope' in options ? !!options.maintainScope : true; logDebug('Debug enabled with options: ' + JSON.stringify(options)); var renameFn = options.renameFn || function(name) { name = name.replace(replaceString, ''); return camelizePluginName ? camelize(name) : name; }; var postRequireTransforms = options.postRequireTransforms || {}; if (typeof options.requireFn === 'function') { requireFn = options.requireFn; } else if (typeof config === 'string') { requireFn = function(name) { // This searches up from the specified package.json file, making sure // the config option behaves as expected. See issue #56. var src = resolve.sync(name, { basedir: path.dirname(config) }); return require(src); }; } else { requireFn = require; } configObject = (typeof config === 'string') ? require(config) : config; if (!configObject) { throw new Error('Could not find dependencies. Do you have a package.json file in your project?'); } var names = scope.reduce(function(result, prop) { return result.concat(Object.keys(configObject[prop] || {})); }, []); logDebug(names.length + ' plugin(s) found: ' + names.join(' ')); pattern.push('!gulp-load-plugins'); function logDebug(message) { if (DEBUG) { logger('gulp-load-plugins: ' + message); } } function defineProperty(object, transform, requireName, name, maintainScope) { var err; if (object[requireName]) { logDebug('error: defineProperty ' + name); err = maintainScope ? 'Could not define the property "' + requireName + '", you may have repeated dependencies in your package.json like' + ' "gulp-' + requireName + '" and ' + '"' + requireName + '"' : 'Could not define the property "' + requireName + '", you may have repeated a dependency in another scope like' + ' "gulp-' + requireName + '" and ' + '"@foo/gulp-' + requireName + '"'; throw new Error(err); } if (lazy) { logDebug('lazyload: adding property ' + requireName); Object.defineProperty(object, requireName, { enumerable: true, get: function() { logDebug('lazyload: requiring ' + name + '...'); return transform(requireName, requireFn(name)); } }); } else { logDebug('requiring ' + name + '...'); object[requireName] = transform(requireName, requireFn(name)); } } function getRequireName(name) { var requireName; if (renameObj[name]) { requireName = options.rename[name]; } else { requireName = renameFn(name); } logDebug('renaming ' + name + ' to ' + requireName); return requireName; } function applyTransform(requireName, plugin) { var transform = postRequireTransforms[requireName]; if (transform && typeof transform === 'function') { // if postRequireTransform function is passed, pass it the plugin and return it logDebug('transforming ' + requireName); return transform(plugin); } else { return plugin; // if no postRequireTransform function passed, return the plugin as is } } var scopeTest = new RegExp('^@'); var scopeDecomposition = new RegExp('^@(.+)/(.+)'); unique(micromatch(names, pattern)).forEach(function(name) { var decomposition; var fObject = finalObject; if (scopeTest.test(name)) { decomposition = scopeDecomposition.exec(name); if (maintainScope) { if (!fObject.hasOwnProperty(decomposition[1])) { finalObject[decomposition[1]] = {}; } fObject = finalObject[decomposition[1]]; } defineProperty(fObject, applyTransform, getRequireName(decomposition[2]), name, maintainScope); } else { defineProperty(fObject, applyTransform, getRequireName(name), name, maintainScope); } }); return finalObject; }; var parentDir = path.dirname(module.parent.filename); // Necessary to get the current `module.parent` and resolve paths correctly. delete require.cache[__filename];