7

Node.js 用于解决调用的算法在伪代码require()中非常清楚地记录了下来。

@import对于 Sass 的声明,我需要同样的东西。

我知道这将尝试在与导入文件相同的目录中以及相对于任何已配置的“加载路径”的基本名称和扩展名的各种组合...但是这些尝试的顺序是什么,即@import 'foo'什么如果有多个文件可以满足?是否以 a 开头或影响它是否尝试加载路径?还有其他我没有涉及的事情吗?文件呢?foo_foo.scss.sass@import./../.css

除了“Sass 很聪明,它会为你解决”之外,该指南并没有说太多。参考文档更详细,但仍然没有说明解决步骤。

任何人都可以用伪代码提供它使用的确切算法吗?

4

1 回答 1

6

这是一个简化的算法@import <import_arg>;。这是通过阅读 SASS 的源代码和运行我自己的测试得出的。

def main(import_arg)
  let dirname = File.dirname(import_arg)
  let basename = File.basename(import_arg)
  if import_arg is absolute ... # omitted as this is a rare case
  else return search(dirname, basename)
end

# try resolving import argument relative to each path in load_paths
# 1. If we encounter an unambiguous match, we finish
# 2. If we encounter an ambiguous match, we give up
# see: https://stackoverflow.com/a/33588202/3649209
def search(dirname, basename)
  let cwd = operating system current working directory
  let load_paths = paths specified via SASS_PATH env variable and via --load-path options
  let search_paths = [cwd].concat(load_paths)
  for path in search_paths
    let file = find_match(File.expand_path(basename, path), basename)
    if (file != false) return file
  end
  throw "File to import not found or unreadable"
end

def find_match(directory, basename)
  let candidates = possible_files(directory, basename)
  if candiates.length == 0
    # not a single match found ... don't give up yet
    return false
  else if candidates.length > 1
    # several matching files, ambiguity! ... give up
    # throw ambiguity error
    throw "It's not clear which file to import"
  else
    # success! exactly one match found
    return candidates[0]
  end
end

# NB: this is a bit tricky to express in code
# which is why I settled for a high-level description
def possible_files(directory, basename)
  # if `basename` is of the form shown on the LHS
  # then check the filesystem for existence of
  # any of the files shown on the RHS within
  # directory `directory`. Return the list all the files
  # which do indeed exist (or [] if none exist).
  #   LHS        RHS
  #   x.sass -> _x.sass, x.sass
  #   x.scss -> _x.scss, x.scss
  #   x      -> x.scss, _x.scss, x.sass, _x.sass
  #   _x     -> _x.scss, _x.sass
end

为简洁起见,我使用的是 Ruby 的File#dirnameFile#basename以及File#expand类似于 Node.js 的path.resolve。我正在使用类似 Ruby 的伪代码,但它仍然是伪代码。

关键点:

  • 没有优先顺序。SASS 没有实施优先顺序,而是在有多个可能的候选者时放弃。例如,如果你写了@import "x"并说两者都x.scss存在_x.scss,那么 sass 会抛出一个歧义错误。类似地,如果两者都x.scss存在,x.sass则会引发歧义错误。
  • 从“从左到右”的顺序尝试加载路径。它们提供了一个根或基础来解析导入(类似于 UNIX 使用 $PATH 查找可执行文件的方式)。当前工作目录总是首先尝试。(尽管此行为将从 3.2更改为 3.4)
  • 无论您是否使用./../
  • 在 sass 文件中,无法导入常规 .css 文件

如果您想了解更多详细信息,我建议您阅读 SASS 的源代码:

  • import方法sass/lib/sass/tree/import_node.rbsearch在第 53-56 行,您可以看到与伪代码中函数内部的 for 循环相同的 for 循环。
  • Importers::Base抽象类sass/lib/sass/importors/base.rb。该文件的注释非常方便。
  • find_real_file方法sass/lib/sass/importors/filesystem.rb。第 112-126 行实现了我们的possible_files功能。第 156 行检查只有一个匹配项。如果没有,则第 167 行会引发歧义错误,否则第 183 行会选择一个匹配的文件。

编辑:我对以前的答案不满意,所以我将其重写为更清晰一些。算法现在可以正确处理文件名中的下划线(以前的算法没有)。我还添加了一些关键点来解决 OP 提出的其他问题。

于 2015-12-18T22:25:47.230 回答