Here is a simplified algorithm for @import <import_arg>;.
This is derived from reading the source code for SASS and from running my own tests.
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
Note for brevity, I am using Ruby's File#dirname, File#basename as well as File#expand which is like Node.js's path.resolve. I'm using a Ruby-like pseudocode but it is still meant to be pseudocode.
Key Points:
- There is no precedence order. Rather than implementing a precedence order, SASS gives up when there are several possible candidates. For example, if you wrote
@import "x" and say both x.scss and _x.scss exist, then sass will throw an ambiguity error. Similarly, if both x.scss and x.sass exist then an ambiguity error is thrown.
- Load paths are tried from 'left to right' order. They provide a root or base to resolve imports from (similar how UNIX use $PATH to find executables). The current working directory is always tried first. (although this behaviour will change from 3.2 to 3.4)
- Load paths are always tried regardless of whether you used
./ or ../
- In a sass file, regular .css files cannot be imported
If you want more detail, I would recommend reading SASS's source code:
- the
import method of sass/lib/sass/tree/import_node.rb. On lines 53-56 you can see the same for loop as the one inside the search function in our pseudocode.
- the
Importers::Base abstract class sass/lib/sass/importors/base.rb. The comments of this file are quite handy.
- the
find_real_file method of sass/lib/sass/importors/filesystem.rb. Lines 112-126 implements our possible_files function. Line 156 checks there is only one match. If there isn't then line 167 throws an ambiguity error, else line 183 picks the one matching file.
Edit: I wasn't happy with my previous answer so I've rewritten it to be a bit clearer. Algorithm now correctly handles underscores in filename (previous algorithm didn't). I also added some key points that address the other questions OP asked.