4

我正在尝试组建一个资源构建系统,该系统将从一组目录中加载 LESS 文件:

Common
├─┬ sub
│ ├── A
│ └── B
├── C
└── ...

每个底层目录都有一个入口点 index.less。索引文件将包含 @import语句,例如@import "colors.less";.

我想要发生的是:

  • 如果导入的文件存在于当前目录中,则使用它。
  • 如果文件不存在,则使用父目录下的同名文件,递归到根目录。

因此,在解析时/Common/sub/A/index.less,请先查找 colors.less in A,然后 in sub,然后再查找Common

我已经开发了一个两阶段构建过程的前半部分:

  1. 扫描整个目录结构并将所有文件加载到一个对象中:

    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';", ... } }
              }
    
  2. 为每个底层目录构建生成的 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 阶段,我可以手动找到@importAST 中的节点并合并到已经解析的 AST 中(递归地,因为它们可以包含自己的@imports)。

4

1 回答 1

0

一个有趣的问题。在不深入研究 Less 解析器的实现(我假设您现在想尽可能避免)的情况下,为什么不简单地添加一个预解析步骤:读取所有 .less 文件并注释掉导入?

因此,在通过解析器运行 less 文件之前,您可以自己读取它并写入文件,注释掉 @import 行,并像您一样记下它们。然后通过解析器运行它,您将获得一个 AST,您可以将之前获取的导入信息附加到该 AST。

现在,您可以按照您已经计划的任何方式将所有 AST 组合在一起。

确保将较少的文件保持在您找到它们的状态。取消注释这些行,或者更好的方法是复制您要处理的每个文件,注释掉导入,解析它,然后删除。这样您就不必担心污染原始文件。

似乎是规避问题的快速方法。除非你宁愿做一些事情,比如告诉 less 解析器本身忽略 @import 标记。(只是在这里随机刺伤,但也许如果您在第 1252 行编辑 lib/less/parser.js 中的“导入”函数以返回 new(tree.Comment)(dir);它可能只是将每个标记解析@import为注释。

于 2013-03-22T03:27:19.843 回答