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://mcmap.net/q/205569/-what-does-gulp-39-s-includepaths-do
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.