| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339 | 'use strict';var $ = {  _: require('lodash'),  fs: require('fs'),  lodash: require('lodash'),  path: require('path'),  glob: require('glob'),  propprop: require('propprop')};/** * Detect dependencies of the components from `bower.json`. * * @param  {object} config the global configuration object. * @return {object} config */function detectDependencies(config) {  var allDependencies = {};  if (config.get('dependencies')) {    $._.assign(allDependencies, config.get('bower.json').dependencies);  }  if (config.get('dev-dependencies')) {    $._.assign(allDependencies, config.get('bower.json').devDependencies);  }  if (config.get('include-self')) {    allDependencies[config.get('bower.json').name] = config.get('bower.json').version;  }  $._.each(allDependencies, gatherInfo(config));  config.set('global-dependencies-sorted', filterExcludedDependencies(    config.get('detectable-file-types').      reduce(function (acc, fileType) {        if (!acc[fileType]) {          acc[fileType] = prioritizeDependencies(config, '.' + fileType);        }        return acc;      }, {}),    config.get('exclude')  ));  return config;}/** * Find the component's JSON configuration file. * * @param  {object} config     the global configuration object * @param  {string} component  the name of the component to dig for * @return {object} the component's config file */function findComponentConfigFile(config, component) {  var componentConfigFile;  if (config.get('include-self') && component === config.get('bower.json').name) {    return config.get('bower.json');  }  ['bower.json', '.bower.json', 'component.json', 'package.json'].    forEach(function (configFile) {      configFile = $.path.join(config.get('bower-directory'), component, configFile);      if (!$._.isObject(componentConfigFile) && $.fs.existsSync(configFile)) {        componentConfigFile = JSON.parse($.fs.readFileSync(configFile));      }    });  return componentConfigFile;}/** * Find the main file the component refers to. It's not always `main` :( * * @param  {object} config        the global configuration object * @param  {string} component     the name of the component to dig for * @param  {componentConfigFile}  the component's config file * @return {array} the array of paths to the component's primary file(s) */function findMainFiles(config, component, componentConfigFile) {  var filePaths = [];  var file;  var self = config.get('include-self') && component === config.get('bower.json').name;  var cwd = self ? config.get('cwd') : $.path.join(config.get('bower-directory'), component);  if ($._.isString(componentConfigFile.main)) {    // start by looking for what every component should have: config.main    filePaths = [componentConfigFile.main];  } else if ($._.isArray(componentConfigFile.main)) {    filePaths = componentConfigFile.main;  } else if ($._.isArray(componentConfigFile.scripts)) {    // still haven't found it. is it stored in config.scripts, then?    filePaths = componentConfigFile.scripts;  } else {    file = $.path.join(config.get('bower-directory'), component, componentConfigFile.name + '.js');    if ($.fs.existsSync(file)) {      filePaths = [componentConfigFile.name + '.js'];    }  }  return $._.unique(filePaths.reduce(function (acc, filePath) {    acc = acc.concat(      $.glob.sync(filePath, { cwd: cwd, root: '/' })        .map(function (path) {          return $.path.join(cwd, path);        })    );    return acc;  }, []));}/** * Store the information our prioritizer will need to determine rank. * * @param  {object} config   the global configuration object * @return {function} the iterator function, called on every component */function gatherInfo(config) {  /**   * The iterator function, which is called on each component.   *   * @param  {string} version    the version of the component   * @param  {string} component  the name of the component   * @return {undefined}   */  return function (version, component) {    var dep = config.get('global-dependencies').get(component) || {      main: '',      type: '',      name: '',      dependencies: {}    };    var componentConfigFile = findComponentConfigFile(config, component);    if (!componentConfigFile) {      var error = new Error(component + ' is not installed. Try running `bower install`.');      error.code = 'PKG_NOT_INSTALLED';      config.get('on-error')(error);      return;    }    var overrides = config.get('overrides');    if (overrides && overrides[component]) {      if (overrides[component].dependencies) {        componentConfigFile.dependencies = overrides[component].dependencies;      }      if (overrides[component].main) {        componentConfigFile.main = overrides[component].main;      }    }    var mains = findMainFiles(config, component, componentConfigFile);    var fileTypes = $._.chain(mains).map($.path.extname).unique().value();    dep.main = mains;    dep.type = fileTypes;    dep.name = componentConfigFile.name;    var depIsExcluded = $._.find(config.get('exclude'), function (pattern) {      return $.path.join(config.get('bower-directory'), component).match(pattern);    });    if (dep.main.length === 0 && !depIsExcluded) {      // can't find the main file. this config file is useless!      config.get('on-main-not-found')(component);      return;    }    if (componentConfigFile.dependencies) {      dep.dependencies = componentConfigFile.dependencies;      $._.each(componentConfigFile.dependencies, gatherInfo(config));    }    config.get('global-dependencies').set(component, dep);  };}/** * Compare two dependencies to determine priority. * * @param  {object} a  dependency a * @param  {object} b  dependency b * @return {number} the priority of dependency a in comparison to dependency b */function dependencyComparator(a, b) {  var aNeedsB = false;  var bNeedsA = false;  aNeedsB = Object.    keys(a.dependencies).    some(function (dependency) {      return dependency === b.name;    });  if (aNeedsB) {    return 1;  }  bNeedsA = Object.    keys(b.dependencies).    some(function (dependency) {      return dependency === a.name;    });  if (bNeedsA) {    return -1;  }  return 0;}/** * Take two arrays, sort based on their dependency relationship, then merge them * together. * * @param  {array} left * @param  {array} right * @return {array} the sorted, merged array */function merge(left, right) {  var result = [];  var leftIndex = 0;  var rightIndex = 0;  while (leftIndex < left.length && rightIndex < right.length) {    if (dependencyComparator(left[leftIndex], right[rightIndex]) < 1) {      result.push(left[leftIndex++]);    } else {      result.push(right[rightIndex++]);    }  }  return result.    concat(left.slice(leftIndex)).    concat(right.slice(rightIndex));}/** * Take an array and slice it in halves, sorting each half along the way. * * @param  {array} items * @return {array} the sorted array */function mergeSort(items) {  if (items.length < 2) {    return items;  }  var middle = Math.floor(items.length / 2);  return merge(    mergeSort(items.slice(0, middle)),    mergeSort(items.slice(middle))  );}/** * Some dependencies which we know should always come first. */var eliteDependencies = [  'es5-shim',  'jquery',  'zepto',  'modernizr'];/** * Sort the dependencies in the order we can best determine they're needed. * * @param  {object} config    the global configuration object * @param  {string} fileType  the type of file to prioritize * @return {array} the sorted items of 'path/to/main/files.ext' sorted by type */ function prioritizeDependencies(config, fileType) {  var eliteDependenciesCaught = [];  var dependencies = mergeSort(    $._.toArray(config.get('global-dependencies').get()).      filter(function (dependency) {        return $._.contains(dependency.type, fileType);      }).      filter(function (dependency) {        if ($._.contains(eliteDependencies, dependency.name)) {          eliteDependenciesCaught.push(dependency.main);        } else {          return true;        }      })    ).map($.propprop('main'));  eliteDependenciesCaught.    forEach(function (dependency) {      dependencies.unshift(dependency);    });  return $._    (dependencies).      flatten().      value().      filter(function (main) {        return $.path.extname(main) === fileType;      });}/** * Excludes dependencies that match any of the patterns. * * @param  {array} allDependencies  array of dependencies to filter * @param  {array} patterns         array of patterns to match against * @return {array} items that don't match any of the patterns */function filterExcludedDependencies(allDependencies, patterns) {  return $._.transform(allDependencies, function (result, dependencies, fileType) {    result[fileType] = $._.reject(dependencies, function (dependency) {      return $._.find(patterns, function (pattern) {        return dependency.replace(/\\/g, '/').match(pattern);      });    });  });}module.exports = detectDependencies;
 |