sass-import-resolver resolves the path of sass imports, following a heavily opinionated and probably very shady algorithm, which I will get to in a bit.
The purpose of the package is to be used for sass importers or similar scripts. The API has similar signature to a sass importer.
npm install @joneff/sass-import-resolver --save-dev
It's probably not a good idea to use in any production code, without rigorous testing. Usage may feel a bit like a Kübler-Ross grief cycle -- denial, anger, barganing, depression, acceptance -- but it's the only sane solution I was able to come up with.
-- me
Something like this will yield results:
const resolver = require('@joneff/sass-import-resolver');
// assuming @import "../some/dependency.scss" in ./my/overly/nested/framework.scss
const file = ...;
const options = ...;
const result = resolver.resolve({ file, ...options });
console.log(result);
// if the file exists => ./my/overly/some/dependecy.scss
// if not => ../some/dependency.scss
The api has only two methods with both using the same params.
- Signature:
function resolve( options: { file: String, prev?: String, includePaths?: Array<String>, nodeModules?: String } ) : String
Just a facade -- passes everything down to _resove
and waits for results.
- Signature:
function _resolve( options: { file: String, prev?: String, includePaths?: Array<String>, nodeModules?: String } ) : Array<String>
Does the actual work, collects unique matches and returns them.
options.file
-- Path to a fileoptions.prev
(Optional) -- Path to file (or dir) to be used for resolvingurl
. Ideally, it should not be empty and should be the previously resolved path.options.includePaths
(Optional) -- An array of paths that the script can look in when attempting to resolve@import
declarations. When resolving node_module (~
), absolute (/
) or parent (../
) imports, this has no effect.options.nodeModules
(Optional) -- Location ofnode_modules
when resolving. Defaults to./node_modules
.
The algorithm is based on Sass @import
documentation, and should work as follows, assuming atleast options.file
param is passed:
- if
file
starts withhttp://
,https://
,//
,\\\\
orurl(
, it's not proccessed at all and returned as is; - if
prev
is file, setcwd
to the directory that file is in; - if
prev
is directory, setcwd
to that directory; - if
prev
is not passed, setcwd
toproccess.cwd()
; - if
includePaths
is not passed, assume it's an empty array; - if
file
is absolute path, clearcwd
andincludePaths
; - if
file
starts with~
, assume node_modules import, setcwd
tonode_modules
and clearincludePaths
; - if
file
starts with.
, clearincludePaths
; - assuming there are any
includePaths
left, unique them withcwd
and loop them:- if the file portion of
file
has.css
,.scss
, or.sass
extension, resolve tha path and return it; - if the file portion
file
starts with_
, resolve and return the following 7 variants in that order:_file.css
_file.scss
_file.sass
file/index.scss
(that's an exception from sass @import)file/index.sass
(that's an exception from sass @import)file/_index.scss
file/_index.sass
- resolve and return the following 9 variants in that order:
file.css
file.scss
file.sass
_file.scss
_file.sass
file/index.scss
(that's an exception from sass @import)file/index.sass
(that's an exception from sass @import)file/_index.scss
file/_index.sass
- if the file portion of
- assuming there is an array of matches, loop over:
- return the resolved path of the first file that exists on the file system
- otherwise return the original
file
Surely, the algorithm can be extended in various directions like scraping the package.json
of resolved modules and looking for entry points, which is not a bad idea at all. However, if you are a sass package creator and you rely on such custom logic, things will not go well for consumers of said packages.
Sure.