text: 'use strict';
  
  var fs = require('fs');
  var path = require('path');
  var _ = require('lodash');
  var glob = require('glob');
  var parseImports = require('./parse-imports');
  
  // resolve a sass module to a path
  function resolveSassPath(sassPath, loadPaths, extensions) {
    // trim sass file extensions
    var re = new RegExp('(\.('+extensions.join('|')+'))$', 'i');
    var sassPathName = sassPath.replace(re, '');
    // check all load paths
    var i, j, length = loadPaths.length, scssPath, partialPath;
    for (i = 0; i < length; i++) {
      for (j = 0; j < extensions.length; j++) {
        scssPath = path.normalize(loadPaths[i] + '/' + sassPathName + '.' + extensions[j]);
        try {
          if (fs.lstatSync(scssPath).isFile()) {
            return scssPath;
          }
        } catch (e) {}
      }
  
      // special case for _partials
      for (j = 0; j < extensions.length; j++) {
        scssPath = path.normalize(loadPaths[i] + '/' + sassPathName + '.' + extensions[j]);
        partialPath = path.join(path.dirname(scssPath), '_' + path.basename(scssPath));
        try {
          if (fs.lstatSync(partialPath).isFile()) {
            return partialPath;
          }
        } catch (e) {}
      }
    }
  
    // File to import not found or unreadable so we assume this is a custom import
    return false;
  }
  
  function Graph(options, dir) {
    this.dir = dir;
    this.extensions = options.extensions || [];
    this.index = {};
    this.follow = options.follow || false;
    this.loadPaths = _(options.loadPaths).map(function(p) {
      return path.resolve(p);
    }).value();
  
    if (dir) {
      var graph = this;
      _.each(glob.sync(dir+'/**/*.@('+this.extensions.join('|')+')', { dot: true, nodir: true, follow: this.follow }), function(file) {
        graph.addFile(path.resolve(file));
      });
    }
  }
  
  // add a sass file to the graph
  Graph.prototype.addFile = function(filepath, parent) {
    var entry = this.index[filepath] = this.index[filepath] || {
      imports: [],
      importedBy: [],
      modified: fs.statSync(filepath).mtime
    };
  
    var resolvedParent;
    var isIndentedSyntax = path.extname(filepath) === '.sass';
    var imports = parseImports(fs.readFileSync(filepath, 'utf-8'), isIndentedSyntax);
    var cwd = path.dirname(filepath);
  
    var i, length = imports.length, loadPaths, resolved;
    for (i = 0; i < length; i++) {
      loadPaths = _([cwd, this.dir]).concat(this.loadPaths).filter().uniq().value();
      resolved = resolveSassPath(imports[i], loadPaths, this.extensions);
      if (!resolved) continue;
  
      // recurse into dependencies if not already enumerated
      if (!_.includes(entry.imports, resolved)) {
        entry.imports.push(resolved);
        this.addFile(fs.realpathSync(resolved), filepath);
      }
    }
  
    // add link back to parent
    if (parent) {
      resolvedParent = _(parent).intersection(this.loadPaths).value();
  
      if (resolvedParent) {
        resolvedParent = parent.substr(parent.indexOf(resolvedParent));
      } else {
        resolvedParent = parent;
      }
  
      entry.importedBy.push(resolvedParent);
    }
  };
  
  // visits all files that are ancestors of the provided file
  Graph.prototype.visitAncestors = function(filepath, callback) {
    this.visit(filepath, callback, function(err, node) {
      if (err || !node) return [];
      return node.importedBy;
    });
  };
  
  // visits all files that are descendents of the provided file
  Graph.prototype.visitDescendents = function(filepath, callback) {
    this.visit(filepath, callback, function(err, node) {
      if (err || !node) return [];
      return node.imports;
    });
  };
  
  // a generic visitor that uses an edgeCallback to find the edges to traverse for a node
  Graph.prototype.visit = function(filepath, callback, edgeCallback, visited) {
    filepath = fs.realpathSync(filepath);
    var visited = visited || [];
    if (!this.index.hasOwnProperty(filepath)) {
      edgeCallback('Graph doesn\'t contain ' + filepath, null);
    }
    var edges = edgeCallback(null, this.index[filepath]);
  
    var i, length = edges.length;
    for (i = 0; i < length; i++) {
      if (!_.includes(visited, edges[i])) {
        visited.push(edges[i]);
        callback(edges[i], this.index[edges[i]]);
        this.visit(edges[i], callback, edgeCallback, visited);
      }
    }
  };
  
  function processOptions(options) {
    return _.assign({
      loadPaths: [process.cwd()],
      extensions: ['scss', 'css', 'sass'],
    }, options);
  }
  
  module.exports.parseFile = function(filepath, options) {
    if (fs.lstatSync(filepath).isFile()) {
      filepath = path.resolve(filepath);
      options = processOptions(options);
      var graph = new Graph(options);
      graph.addFile(filepath);
      return graph;
    }
    // throws
  };
  
  module.exports.parseDir = function(dirpath, options) {
    if (fs.lstatSync(dirpath).isDirectory()) {
      dirpath = path.resolve(dirpath);
      options = processOptions(options);
      var graph = new Graph(options, dirpath);
      return graph;
    }
    // throws
  };
  