hexo/node_modules/sass-graph/sass-graph.js

172 lines
5.3 KiB
JavaScript
Raw Normal View History

2023-10-03 11:14:36 +08:00
'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.exclude = options.exclude instanceof RegExp ? options.exclude : null;
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) {
try {
graph.addFile(path.resolve(file));
} catch (e) {}
});
}
}
// add a sass file to the graph
Graph.prototype.addFile = function(filepath, parent) {
if (this.exclude !== null && this.exclude.test(filepath)) return;
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;
// check exclcude regex
if (this.exclude !== null && this.exclude.test(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;
}
// check exclcude regex
if (!(this.exclude !== null && this.exclude.test(resolvedParent))) {
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 Object.assign({
loadPaths: [process.cwd()],
extensions: ['scss', '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
};