Reputation: 8433
Currently in our Sass files we have something like the following:
@import "../../node_modules/some-module/sass/app";
This is bad, because we're not actually sure of the path: it could be ../node_modules
, it could be ../../../../../node_modules
, because of how npm installs stuff.
Is there a way in Sass that we can search up until we find node_modules? Or even a proper way of including Sass through npm?
Upvotes: 55
Views: 63267
Reputation: 91
All previous answers are outdated as of February of 2024
Additional information available here
The recommended approach is to use the pkg:
protocol.
I.e.
@use 'pkg:my-npm-pkg';
/* Or */
@import 'pkg:my-npm-pkg';
The sass compiler has to be configured with a importers
as
sass.compile('my-file.scss', {
importers: [
new sass.NodePackageImporter()
]
});
Upvotes: 1
Reputation: 11
From offical docuumentation of Sass, adding ~
to imports should do the job.
However, for some reason it did'nt work for me, and sass compiler still complains that the module cannot be found.
Hence, I tried another method which worked for me without any issues. Here's the solution:
sass src/main.scss dist/main.css --load-path=node_modules
scripts
of package.json
: "scripts": {
...
"build": "sass src/main.scss dist/main.css --load-path=node_modules",
...
}
Then Run:
npm run build
@import "some-module/sass/app";
To wrap it up, adding --load-path=node_modules
flag solved the issue permanently. For more information you can check:
sass --help
Upvotes: 1
Reputation: 616
For dart-sass
and commandline user at 2022, just use the --load-path
option:
$ npx sass --load-path=node_modules
Important: the whole node_modules folder contains so much, just set it launch extremely slow in watch mode. Your should only set your package paths, eg:
$npx sass -w --load-path=node_modules/foo --load-path=node_modules/bar/scss
Upvotes: 5
Reputation: 15836
If you are looking for a handy answer in 2017 and are using Webpack, this was the easiest I found.
Suppose your module path is like:
node_modules/some-module/sass/app
Then in your main scss file you can use:
@import "~some-module/sass/app";
Tilde operator shall resolve any import as a module.
Upvotes: 80
Reputation: 123198
I made the sass-npm module specifically for this.
npm install sass-npm
In your SASS:
// Since node_modules/npm-module-name/style.scss exists, this will be imported.
@import "npm-module-name";
// Since just-a-sass-file isn't an installed npm module, it will be imported as a regular SCSS file.
@import "just-a-sass-file";
I normally use gulp-sass (which has the same 'importer' option as regular SASS)
var gulp = require('gulp'),
sass = require('gulp-sass'),
sassNpm = require('sass-npm')();
Then, in your .pipe(sass())
, add the importer as an option:
.pipe(sass({
paths: ['public/scss'],
importer: sassNpm.importer,
}))
Upvotes: 5
Reputation: 366
As Oncle Tom mentioned, the new version of Sass has this new importer
option, where every "import" you do on your Sass file will go first through this method. That means that you can then modify the actual url of this method.
I've used require.resolve
to locate the actual module entry file.
Have a look at my gulp task and see if it helps you:
'use strict';
var path = require('path'),
gulp = require('gulp'),
sass = require('gulp-sass');
var aliases = {};
/**
* Will look for .scss|sass files inside the node_modules folder
*/
function npmModule(url, file, done) {
// check if the path was already found and cached
if(aliases[url]) {
return done({ file:aliases[url] });
}
// look for modules installed through npm
try {
var newPath = path.relative('./css', require.resolve(url));
aliases[url] = newPath; // cache this request
return done({ file:newPath });
} catch(e) {
// if your module could not be found, just return the original url
aliases[url] = url;
return done({ file:url });
}
}
gulp.task("style", function() {
return gulp.src('./css/app.scss')
.pipe(sass({ importer:npmModule }))
.pipe(gulp.dest('./css'));
});
Now let's say you installed inuit-normalize
using node. You can simply "require" it on your Sass file:
@import "inuit-normalize";
I hope that helps you and others. Because adding relative paths is always a pain in the ass :)
Upvotes: 18
Reputation: 705
You can add another includePaths to your render options.
Plain example
Snippet based on example from Oncle Tom.
var options = {
file: './sample.scss',
includePaths: [
path.join(__dirname, 'bower_components'), // bower
path.join(__dirname, 'node_modules') // npm
]
};
sass.render(options, function(err, result){
console.log(result.css.toString());
});
That should do. You can include the files from package using @import "my-cool-package/super-grid
Webpack and scss-loader example
{
test: /\.scss$/,
loader: 'style!css!autoprefixer?browsers=last 2 version!sass?outputStyle=expanded&sourceMap=true&sourceMapContents=true&includePaths[]=./node_modules'
},
Notice the last argument, includePaths has to be array. Keep in mind to use right format
Upvotes: 18
Reputation: 1879
You can use a Sass importer
function to do so. Cf. https://github.com/sass/node-sass#importer--v200.
The following example illustrates [email protected] with [email protected]:
Install the bower dependency:
$ bower install sass-mq
$ npm install sass/node-sass#3.0.0-pre
The Sass file:
@import 'sass-mq/mq';
body {
@include mq($from: mobile) {
color: red;
}
@include mq($until: tablet) {
color: blue;
}
}
The node renderer file:
'use strict';
var sass = require('node-sass');
var path = require('path');
var fs = require('fs');
var options = {
file: './sample.scss',
importer: function bowerModule(url, file, done){
var bowerComponent = url.split(path.sep)[0];
if (bowerComponent !== url) {
fs.access(path.join(__dirname, 'bower_components', bowerComponent), fs.R_OK, function(err){
if (err) {
return done({ file: url });
}
var newUrl = path.join(__dirname, 'bower_components', url);
done({ file: newUrl });
})
}
else {
done({ file: url });
}
}
};
sass.render(options, function(err, result){
if (err) {
console.error(err);
return;
}
console.log(result.css.toString());
});
This one is simple and not recursive. The require.resolve
function could help to deal with the tree – or wait until [email protected] to benefit from the flat dependency tree.
Upvotes: 8