55

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?

callumacrae
  • 8,185
  • 8
  • 32
  • 49
  • possible duplicate of [SASS: Import a file from a different directory?](http://stackoverflow.com/questions/6502313/sass-import-a-file-from-a-different-directory) – cimmanon Apr 10 '15 at 17:44
  • 4
    @cimmanon: What? No, that's a completely different question… – callumacrae Apr 12 '15 at 15:05

7 Answers7

78

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.

CTarczon
  • 898
  • 9
  • 19
ProllyGeek
  • 15,517
  • 9
  • 53
  • 72
  • 1
    simplest of all :) – Shreyas Apr 05 '17 at 08:39
  • 18
    It seems to me this only works automatically if you're using Webpack. – CTarczon May 02 '17 at 21:19
  • 11
    @CTarczon this should be mentioned in the answer, many people, myself included, do not use Webpack to compile their Sass. – ESR May 29 '17 at 14:17
  • 2
    I find myself here a year later, now using Webpack...but why does this solution work? – ESR Jul 11 '18 at 06:50
  • FYI - if you're using Parcel bundler instead of webpack, you can omit the tilde ("~"). – Brian Zelip Mar 27 '19 at 19:26
  • Thanks, great, Now please who knows how to import SASS from another domain. Just the way you have ---> `@import "~some-module/sass/app";` ---> `@import "?http://some-site.com/sass/app";` – Vixson Aug 20 '20 at 15:48
18

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 :)

Shaun Luttin
  • 133,272
  • 81
  • 405
  • 467
Lucas Motta
  • 366
  • 3
  • 9
18

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

Kamil Jopek
  • 705
  • 6
  • 9
  • This worked for me. Here are more examples of how to specify sass-loader options: https://github.com/jtangelder/sass-loader#sass-options – Max Mumford Sep 01 '16 at 09:07
8

You can use a Sass importer function to do so. Cf. https://github.com/sass/node-sass#importer--v200.

The following example illustrates node-sass@3.0.0 with node@0.12.2:

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 npm@3.0.0 to benefit from the flat dependency tree.

Thom4
  • 1,819
  • 17
  • 15
5

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,
}))
anotherdave
  • 6,656
  • 4
  • 34
  • 65
mikemaccana
  • 110,530
  • 99
  • 389
  • 494
  • Please fill in the repository field in your package.json! – callumacrae Nov 23 '15 at 13:26
  • Seems pretty similar to the one I wrote, except mine gets the path from the package.json instead of attempting to require styles.scss: https://gist.github.com/callumacrae/09de2b8294339d6a6785 You've written "sass-npm-import" in your docs but it's actually called "sass-npm", by the way. – callumacrae Nov 23 '15 at 13:27
  • @callumacrae repository field added, and README updated, cheers! If you want to add getting sass from `package.json` as a PR go for it! – mikemaccana Nov 23 '15 at 14:49
  • None of the systems above describe how to declare what the "main" css file of a package is. How does (i.e. `sass-npm`) infer this? – FreePender May 11 '16 at 14:10
  • @freepender see the README. – mikemaccana May 11 '16 at 22:01
4

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 
LitileXueZha
  • 512
  • 6
  • 11
0

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:

  • If you are compiling sass files directly from CLI try this:
sass src/main.scss dist/main.css --load-path=node_modules 
  • If you are using npm and/or webpack for compiling sass files, add something like this to the scripts of package.json:
  "scripts": {
    ...
    "build": "sass src/main.scss dist/main.css --load-path=node_modules",
    ...
  }

Then Run:

npm run build
  • Finally, import your modules like this:
@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