A Custom Sass Import Resolver with included support for Node Module Resolution, additional file extensions, and path aliases/path mapping
Description
This is an implementation of the Sass Import Resolve algorithm, with added support for Node Module Resolution, and path mapping/path aliasing.
At the moment, without this Custom importer, to import files via Node Module Resolution, Sass library creators and consumers can:
- Ship
.scss
, .sass
, or .css
files via node_modules
, and let consumers depend on files directly, - but not support hoisted dependencies/monorepos.
- Use
Eyeglass
One major use case for this library is to support path mapping, which can be useful for many things, includiong simplifying import paths or dividing packages in a monorepo.
This implementation follows the convention of existing tooling and similar solutions that paths with a leading ~
will be resolved via Node Module Resolution.
Features
Backers
Become a sponsor/backer and get your logo listed here.
Patreon
Table of Contents
Install
npm
$ npm install sass-extended-importer --save-dev
Yarn
$ yarn add sass-extended-importer --dev
pnpm
$ pnpm add sass-extended-importer --save-dev
Usage
Usage with sass
To use it with the primary sass
package (dart-sass), simply import the createImporter
function from this package and invoke it with the options you want to provide.
Then pass it to sass
is an import resolver:
import {createImporter} from "sass-extended-importer";
import sass from "sass";
sass({
file: "/path/to/your/file.scss",
importer: createImporter({
// options
})
});
Usage with node-sass
To use it with node-sass
, simply import the createImporter
function from this package and invoke it with the options you want to provide.
Then pass it to node-sass
is an import resolver:
import {createImporter} from "sass-extended-importer";
import sass from "node-sass";
sass({
file: "/path/to/your/file.scss",
importer: createImporter({
// options
})
});
Resolving files within Node Modules
Prefix the path with a ~
to indicate that the Node Module Resolution algorithm should be used:
@import "~my-library";
The resolve function will use Node Module Resolution to find the library, and then look at the main
property within the related package.json
file.
If it points to a file, for example index.js
, for which there is an identically named file with an extension of .scss
, .sass
, or .css
, or if the main
property directly points to a file with a supported extension, that file will be resolved. Alternatively, if the main property is left out, it will default to looking for a file called index
with one of the supported extensions directly within the package folder.
This means that all of the following examples will work:
// Uses the package.json file's main property to look for a file with a supported extension.
// Defaults to looking for a file named `index` with a supported extension
@import "~my-library";
// Specifically looks for a file called 'index' inside the package folder, with a supported extension
@import "~my-library/index";
// Specifically looks for a file with the filename `index.scss`
@import "~my-library/index.scss";
// Looks inside the /foo folder from the package folder and for a file called `bar` with a supported extension
// (or if bar is a folder, a file within it called `index` with a supported extension)
@import "~my-library/foo/bar";
Customizing the prefix
The default prefix of ~
(tilde) is a convention used by several popular tools and is the general recommendation. However, you can customize it with the nodeModuleResolutionPrefix
option for the importer:
import {createImporter} from "sass-extended-importer";
import sass from "sass";
sass({
// ...
importer: createImporter({
// Use # instead of ~
nodeModuleResolutionPrefix: "#"
})
});
Path mapping/aliasing
You can use path mapping to map import paths to other import paths.
This can be useful to simplify import statements, or if you use sass/scss/css in combination with a build pipeline that performs path mapping on your other application- or library code.
For example, you might be using TypeScript's path mapping feature, and want to make sure the same paths are supported inside the sass/scss/css files you're importing from there.
You can customize it with the paths
option for the importer:
import {createImporter} from "sass-extended-importer";
import sass from "sass";
sass({
// ...
importer: createImporter({
paths: {
"my-alias": ["../other-folder/src/index.scss"],
"my-alias/*": ["../other-folder/src/*"]
}
})
});
This allows you do write styles such as:
@import "my-alias/foo";
Which will actually be mapped to ../other-folder/src/foo.scss
.
Adjusting allowed extensions
You can alter what kind of extensions that can be resolved via the extensions
option for the importer:
import {createImporter} from "sass-extended-importer";
import sass from "sass";
sass({
// ...
importer: createImporter({
// Use # instead of ~
extensions: [".myextension", ".foo", ".bar"]
})
});
Customizing the file system
If you use a virtual file system, such as one that exists within memory, you can pass it in as the fileSystem
option for the importer. If you don't, it defaults to using the fs
module:
import {createImporter} from "sass-extended-importer";
import sass from "sass";
import path from "path";
import {createFsFromVolume, Volume} from "memfs";
const volume = new Volume();
vol.mkdirSync("/my/directory", {recursive: true});
vol.writeFileSync("/my/directory/foo.scss", "p {color: red}");
const fileSystem = createFsFromVolume(vol);
sass({
// ...
importer: createImporter({
// Use another file system
fileSystem
})
});
Usage with other tools and libraries
The resolve algorithm implemented by this library is also exported as as helper function, resolve
, that can be used outside of just as a Custom Importer for Sass.
To use it directly, simply import resolve
:
import {resolve} from "sass-extended-importer";
resolve("~my-library", {cwd: "/path/to/directory"});
Contributing
Do you want to contribute? Awesome! Please follow these recommendations.
Maintainers
FAQ
License
MIT © Frederik Wessberg (@FredWessberg) (Website)