text: 'use strict';
  
  module.exports = Component;
  
  var assert     = require('assert');
  var debug      = require('debug')('bower-files');
  var path       = require('path');
  var util       = require('util');
  var arrify     = require('arrify');
  var globby     = require('globby');
  var isAbsolute = require('is-absolute');
  var isSymlink  = require('is-symlink-sync');
  var assign     = require('object-assign');
  var readJson   = require('read-json-sync');
  var minimatch  = require('minimatch');
  
  function Component(options) {
  
    options = normalizeOptions(options);
  
    try {
      this.json = readJson(path.resolve(options.dir, options.json));
    } catch (err) {
      this.json = {};
    }
  
    var dirName = path.basename(options.dir);
    this.name = options.isRoot // TODO deprecate 'self' name
      ? 'self'
      : this.json.name || dirName;
    options.overrides = assign({}, this.json.overrides, options.overrides);
    assign(this.json, options.overrides[dirName], options.overrides[this.name]);
  
    this.files = function (filterOptions) {
      filterOptions = assign({
        fileListProps: ['main'],
        ignoreListProps: [],
        firstFileListProp: true
      }, filterOptions);
      var props = filterOptions.firstFileListProp
        ? find(filterOptions.fileListProps, function (prop) {
            return typeof this.json[prop] !== 'undefined';
          }, this)
        : filterOptions.fileListProps;
      var files = arrify(props).reduce(function (fileList, prop) {
        return fileList.concat(this.json[prop] || []);
      }.bind(this), []);
      var ignore = arrify(filterOptions.ignoreListProps)
        .reduce(function (ignoreList, prop) {
          return ignoreList.concat(this.json[prop] || []);
        }.bind(this), []);
      return Component.mainFiles(options.dir, files, ignore);
    }.bind(this);
  
    this.dependencies = getDependencies(this.json.dependencies, options);
    if (options.isRoot) {
      this.devDependencies = getDependencies(this.json.devDependencies, options);
    }
  
    Object.defineProperties(this, {
      dir: {
        get: util.deprecate(function () {
          return options.dependencyDir;
        }, 'component.dir: access to dir is deprecated')
      },
      path: {
        get: util.deprecate(function () {
          return options.dir;
        }, 'component.path: access to path is deprecated')
      }
    });
  }
  
  Component.mainFiles = function mainFiles(dir, mainDef, ignore) {
    if (!mainDef) { return []; }
    return globby
      .sync(arrify(mainDef), {cwd: dir})
      .filter(function (file) {
        return !ignore.some(function (ignorePattern) {
          return minimatch(file, ignorePattern);
        });
      })
      .map(function (file) {
        return path.resolve(dir, file);
      });
  };
  
  Component.prototype = {
    getDependencies: function (options) {
      options = assign({
        self: false,
        dev: false,
        main: true
      }, options);
  
      if (options.self && !this.json.main) {
        debug(this.json.name + ' does not have a main property. You may need to override this to include this component\'s files'); /* jshint ignore:line */
      }
  
      var components = [];
      if (options.main) {
        components = components.concat(this.dependencies || []);
      }
      if (options.dev) {
        var devDependencies = this.devDependencies || [];
        components = options.dev === 'after'
          ? components.concat(devDependencies)
          : devDependencies.concat(components);
      }
  
      var dependencies = components
        .reduce(function (deps, dep) {
          return deps.concat(dep.getDependencies({self: true}));
        }, [])
        .concat(options.self ? this : []);
      var depNames = dependencies.map(function (dep) { return dep.name; });
      return dependencies.filter(function (dep, i) {
        return depNames.indexOf(dep.name) === i;
      });
    }
  };
  
  function getDependencies(dependencies, options) {
    dependencies = dependencies || {};
    return Object.keys(dependencies).map(function (key) {
      return new Component(assign({}, options, {
        dir: path.resolve(options.dependencyDir, key),
        isRoot: false
      }));
    });
  }
  
  function normalizeOptions(options) {
    options = assign({
      dir: null,
      dependencyDir: null,
      json: 'bower.json',
      componentJson: '.bower.json',
      overrides: {},
      isRoot: false
    }, options);
  
    assert(
      isAbsolute(options.dir || ''),
      'options.dir must be absolute'
    );
  
    assert(
      isAbsolute(options.dependencyDir || ''),
      'options.dependencyDir must be absolute'
    );
  
    options.json = (options.isRoot || isSymlink(options.dir))
      ? options.json
      : options.componentJson;
  
    return options;
  }
  
  function find(arr, fn, context) {
    var found;
    arr.some(function(val, i, orig) {
      var isFound = fn.call(context, val, i, orig);
      if (isFound) {
        found = val;
        return true;
      }
    });
    return found;
  }
  