我试图把一个资源构建系统,该系统由一组目录加载LESS文件:如何推迟LESS解析器对@import规则的评估?
Common
├─┬ sub
│ ├── A
│ └── B
├── C
└── ...
每个底部级目录将有一个切入点,index.less。索引文件将包括@import
报表,例如@import "colors.less";
。
我想发生的事情是:
- 如果导入的文件在当前目录存在,则使用。
- 如果该文件不存在,则使用父目录中相同名称的文件,递归到根目录。
所以解析/Common/sub/A/index.less
时,寻找colors.less在A
,然后在sub
,然后Common
。
我已经制定了两个阶段的构建过程的前半部分:
扫描整个目录结构和所有文件加载到一个对象:
common = { files: { "colors.less": "/* LESS file contents */", ... }, sub: { files: { ... }, A: { files: { "index.less": "@import 'colors.less';", ... } }, B: { files: { "index.less": "@import 'colors.less';", ... } } }, C: { files: { "index.less": "@import 'colors.less';", ... } } }
构建导致每个底层目录的CSS文件。
第二阶段是我遇到过一些问题。首先,我创建一个解析器。
var parser = new less.Parser({
filename: 'index.less'
});
然后解析文件:
parser.parse(common.sub.A.files['index.less'], function(e, tree) {
// `tree` is the AST
});
这会让我们的抽象语法树(AST)传递给回调。问题在于LESS解析器解析了它自己的文件导入器找到的所有@import
语句,并将导入的文件合并到当前的AST中。
要解决这个问题,我目前超载进口商重写路径:
// before starting anything:
var importer = less.Parser.importer;
less.Parser.importer = function(path, currentFileInfo, callback, env) {
var newPath;
// here, use the object from phase 1 to resolve the path nearest file
// matching `path` (which is really just a filename), and set `newPath`
importer(newPath, currentFileInfo, callback, env);
};
然而,LESS进口商还是从磁盘读取文件。这从性能角度来看是不好的,因为(A)我们已经在内存中拥有文件内容,并且(B)底层目录数量很多,因此我们不得不重新加载并重新解析相同的公共文件多次。
我想要做的是解析第一阶段的每个LESS文件,然后在阶段二期间根据需要合并AST。
为了做到这一点,我需要防止LESS在解析过程中评估@import
节点。然后在阶段2中,我可以手动查找AST中的@import
节点并合并到已经解析的AST中(递归地,因为它们可以包含它们自己的@import
)。