2015-12-17 10 views
6

Node.jsがrequire()コールを解決するために使用するアルゴリズムは、非常に明確にdocumented in pseudocodeです。Sassが@import文を解決するために使用するアルゴリズムとは何ですか?

Sassの@import文で同じことが必要です。

私は@import 'foo'がインポートファイルと同様に構成された「ロード・パス」のいずれかに対する同じディレクトリに、拡張子.scss.sassで、ベース名foo_fooのさまざまな組み合わせを試すだろうことを知っている...しかし、どのような順序で試されるか、すなわち@importを満たすことができる複数のファイルがある場合、優先順位はどのようなものか? ./または../で始まるのがロードパスを試行するかどうかに影響しますか?私がカバーしていない他の何かを試みますか? .cssファイルはどうですか?

guideは、「サスが賢明で、あなたのためにそれを理解する」ということをはるかに超えていません。 reference docsはさらに詳しく説明されていますが、解決策のステップはまだありません。

擬似コードで使用する正確なアルゴリズムは誰でも提供できますか?

+0

あなたはSassがオープンソースであり、これを自分自身で見つけることができることを理解していますか? – cimmanon

+0

@cimmanon yep。擬似コードにもそれに価値があるとは思わない? – callum

答えて

5

@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などのNode.jsのpath.resolveのようなものですFile#expand。私はRubyのような擬似コードを使用していますが、まだ擬似コードであることを意図しています。

キーポイント:

  • 何の優先順位はありません。優先順位を実装するのではなく、いくつかの候補がある場合、SASSは中止します。たとえば、@import "x"と書いて、x.scss_x.scssの両方があるとすると、sassはあいまいなエラーを投げます。同様に、x.scssx.sassの両方が存在する場合、あいまいなエラーがスローされます。
  • ロードパスは、「左から右」の順序で試行されます。これらは、インポートを解決するためのルートまたはベースを提供します(UNIXが$ PATHを使用して実行可能ファイルを見つける方法と同様)。現在の作業ディレクトリは常に最初に試行されます。 (3.4から3.2からこの動作will changeが)
  • ロードパスは常に、あなたがより多くの詳細が必要な場合./やSASSファイルで
  • ../、通常の.cssファイル

cannot be importedを使用したかどうかにかかわらず、試されます

  • sass/lib/sass/tree/import_node.rbimport方法:、私はSASSのソースコードを読むことをお勧めします。 53-56行目では、擬似コードのsearch関数内のループと同じループを見ることができます。
  • Importers::Base抽象クラスsass/lib/sass/importors/base.rb。このファイルのコメントはとても便利です。
  • find_real_file方法sass/lib/sass/importors/filesystem.rb。 112-126行目にはpossible_files機能が実装されています。ライン156は、一致が1つしかないことをチェックする。そうでなければ、線167は曖昧さのエラーを投げ、そうでなければ線183は1つの一致するファイルを選ぶ。

編集:私は少しより明確にするためにそれを書き換えてきたので、私は私の前の回答と幸せではなかったです。アルゴリズムはファイル名の下線を正しく処理するようになりました(以前のアルゴリズムではできませんでした)。私はまたOPが尋ねた他の質問に対処するいくつかの重要なポイントを追加しました。

+0

'let dirname = File.dirname(import_arg)'という行は私を混乱させます。インポートされるファイルではなく、インポートするファイルのdirnameを必要としませんか? – callum

+0

また、 'dirname'変数はどこにも使われていないようです。それは 'search()'に渡されますが、その関数はそれを無視します。 – callum