node-sass-self-contained-importer
Version:
Used to execute sass/scss import and meanwhile resolve relative asset paths declared in the imported file as relative to the importing file. This plugin works as an importer for node-sass, so it supports gulp.js.
178 lines (145 loc) • 5.61 kB
JavaScript
var path = require("path"),
lib = require("./lib.js"),
TreeNode = require("./TreeNode.js");
var ROOT = "#|ROOT|#";
/**
* 从路径上最远的结点开始,依次计算并设置路径上每个结点的绝对路径
* @param {TreeNode} node 路径上最远的结点
* @param {String} [entryFileAbsolutePath] 入口文件的绝对路径
*/
var calcAndSetAbsolutePathForEachNodeInThePath = function(node, entryFileAbsolutePath){
entryFileAbsolutePath = entryFileAbsolutePath || path.resolve(process.cwd(), "ROOT.SCSS");
var folder = path.dirname(entryFileAbsolutePath);
lib.logger.debug("Set resolving folder as: {}. Current tree: {}", folder, node.getPathString());
var treeNodes = node.getNodeArrayFromCurrentNodeToRootNode().reverse();
for(var i = 0; i < treeNodes.length; i++){
/* 确定声明import的文件所在的目录 */
var _node = treeNodes[i];
var _importedFileAbsolutePath = _node.getAttachedData();
if(null != _importedFileAbsolutePath){
folder = path.dirname(_importedFileAbsolutePath);
lib.logger.debug("Update resolving folder as: {} by node: [{}]", folder, _node.getValue());
continue;
}
var importedLiteral = _node.getValue();
if(importedLiteral === ROOT)
continue;
lib.logger.debug("Resolving file path for import: '{}'. Resolving folder: {}", importedLiteral, folder);
/* 确定import的目标文件的绝对路径 */
var importedFileAbsolutePath = path.resolve(folder, importedLiteral);
var existingImportedFileAbsolutePath = lib.findMatchingFilePath(importedFileAbsolutePath);
if(null != existingImportedFileAbsolutePath){
_node.setAttachedData(existingImportedFileAbsolutePath);
folder = path.dirname(existingImportedFileAbsolutePath);
}else
lib.logger.error("No scss file found for import: '{}'. Resolving folder: {}", importedLiteral, folder);
}
};
/**
* SCSS引入解析器,用于判定特定由import语法引入路径对应的完整
*/
var ScssImportCursor = function(){
var tree = TreeNode.newNode(ROOT);
/* 入口文件的绝对路径 */
var entryFileAbsolutePath = path.resolve(process.cwd(), "ROOT.scss");
var pointer = tree;
/**
* 获取树根结点
* @returns {TreeNode} 树根结点
*/
this.getTreeRootNode = function(){
return tree;
};
/**
* 获取当前指针位置
* @returns {TreeNode} 当前指针所指向的结点
*/
this.getPointer = function(){
return pointer;
};
/**
* 获取入口文件的绝对路径
* @returns {String}
*/
this.getEntryFileAbsolutePath = function(){
return entryFileAbsolutePath;
};
/**
* 设置入口文件的绝对路径
* @param {String} _entryFileAbsolutePath 入口文件的绝对路径
* @returns {String}
*/
this.setEntryFileAbsolutePath = function(_entryFileAbsolutePath){
entryFileAbsolutePath = _entryFileAbsolutePath;
return this;
};
/**
* 处理scss引入动作,根据import构建关联关系树
* @param {String} url 要引入的路径
* @param {String} pre 声明引入路径的上一个路径
* @returns {{urlNode: {TreeNode}, preNode: {TreeNode}}} 上一个路径对应的结点
*/
this.buildTree = function(url, pre){
lib.logger.debug(">> Build tree for import: '{}' by '{}'. Current tree: {}", url, pre, pointer.getPathString());
var obj = {};
var node = pointer.findNodeByValueFromCurrentNodeToRootNode(pre);
if(null == node){
if(null != pre){
node = TreeNode.newNode(pre);
pointer.addNext(node);
}else
node = tree;
}
pointer = node;
obj.preNode = node;
var _node = TreeNode.newNode(url);
node.addNext(_node);
pointer = _node;
obj.urlNode = _node;
/* 推演关系树上各个结点对应文件的绝对路径 */
calcAndSetAbsolutePathForEachNodeInThePath(obj.urlNode, entryFileAbsolutePath);
lib.logger.debug("<< Build tree for import: '{}' by '{}'. Current connection: {}", url, pre, pointer.getPathString());
return obj;
};
};
/**
* 根据给定的scss文件正文和路径,结合 import 语法搜索该scss依赖的其它文件
* @param {String} scssFileAbsolutePath 要搜索的入口scss文件的绝对路径
* @param {String} [scssFileContent] 要搜索的入口scss文件正文。缺省时,将自动读取 scssFileAbsolutePath 的正文
* @returns {string[]} 依赖的其它scss文件的绝对路径列表
*/
ScssImportCursor.searchImportedScssFiles = function(scssFileAbsolutePath, scssFileContent){
var cursor = new ScssImportCursor().setEntryFileAbsolutePath(scssFileAbsolutePath);
var search = function(scssFileAbsolutePath, scssFileContent, pre){
var arr = [];
if(null === scssFileAbsolutePath || "" === scssFileAbsolutePath.trim())
return arr;
if(arguments.length < 3)
pre = null;
if(arguments.length < 2){
scssFileContent = lib.readFile(scssFileAbsolutePath, false);
if(null === scssFileContent)
return arr;
}
var r = /@import\s+['"]\s*([^'"]+)\s*['"]/gim;
var tmp;
while((tmp = r.exec(scssFileContent)) != null){
var url = tmp[1];
var urlNode = cursor.buildTree(url, pre).urlNode;
var importedFileAbsolutePath = urlNode.getAttachedData();
if(null == importedFileAbsolutePath)
continue;
arr.push(importedFileAbsolutePath);
/* 执行 深度优先 搜索 */
var importedFileContent = lib.readFile(importedFileAbsolutePath, false);
if(null !== importedFileContent)
arr = arr.concat(search(importedFileAbsolutePath, importedFileContent, url));
}
return arr;
};
if(arguments.length > 1)
return search(scssFileAbsolutePath, scssFileContent);
else
return search(scssFileAbsolutePath);
};
module.exports = ScssImportCursor;