diff options
Diffstat (limited to 'node_modules/sass-graph/sass-graph.js')
-rw-r--r-- | node_modules/sass-graph/sass-graph.js | 160 |
1 files changed, 160 insertions, 0 deletions
diff --git a/node_modules/sass-graph/sass-graph.js b/node_modules/sass-graph/sass-graph.js new file mode 100644 index 0000000..5d5aba0 --- /dev/null +++ b/node_modules/sass-graph/sass-graph.js @@ -0,0 +1,160 @@ +'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 +}; |