sass-import-resolver
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.Purpose
The purpose of the package is to be used for sass importers or similar scripts. The API has similar signature to a sass importer.Installation
npm install @joneff/sass-import-resolver --save-dev
Basic Usage
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
API
The api has only two methods with both using the same params.resolve()
- 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.resolve()
- 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
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 nodemodule (~
), absolute (/
) or parent (../
) imports, this has no effect.options.nodeModules
(Optional) -- Location ofnode_modules
when resolving. Defaults to./node_modules
.
Heavily Opinionated and Probably Very Shady Algorithm
The algorithm is based on Sass@import
documentation, and should work as follows, assuming atleast options.file
param is passed:1) if
file
starts with http://
, https://
, //
, \\\\
or url(
, it's not proccessed at all and returned as is;
2) if prev
is file, set cwd
to the directory that file is in;
3) if prev
is directory, set cwd
to that directory;
4) if prev
is not passed, set cwd
to proccess.cwd()
;
5) if includePaths
is not passed, assume it's an empty array;
6) if file
is absolute path, clear cwd
and includePaths
;
7) if file
starts with ~
, assume nodemodules import, set cwd
to node_modules
and clear includePaths
;
8) if file
starts with .
, clear includePaths
;
9) assuming there are any includePaths
left, unique them with cwd
and loop them:1) if the file portion of `file` has `.css`, `.scss`, or `.sass` extension, resolve tha path and return it;
2) if the file portion `file` starts with `_`, resolve and return the following 7 variants in that order:
1) `_file.css`
2) `_file.scss`
3) `_file.sass`
4) `file/index.scss` (that's an exception from sass @import)
5) `file/index.sass` (that's an exception from sass @import)
6) `file/_index.scss`
7) `file/_index.sass`
3) resolve and return the following 9 variants in that order:
1) `file.css`
2) `file.scss`
3) `file.sass`
4) `_file.scss`
5) `_file.sass`
6) `file/index.scss` (that's an exception from sass @import)
7) `file/index.sass` (that's an exception from sass @import)
8) `file/_index.scss`
9) `file/_index.sass`
10) assuming there is an array of matches, loop over:1) return the resolved path of the first file that exists on the file system
2) 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.