Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to @import external SCSS properly with webpack and Vue.js?

As in Material Component Web's example, I want to be able to import SCSS from my node_modules like this:

@import '@material/elevation/mdc-elevation';

However, I'm getting this error message when trying to run the webpack build:

File to import not found or unreadable: @material/elevation/mdc-elevation.

@import './~/@material/elevation/mdc-elevation.scss'; doesn't work either.

I'm pretty sure the issue is somewhere in my webpack config, but I can't figure out where.

What did they do in Material Components Web's Vue.js example in order to make it work?

Here's my npm-debug.log in case you need it. And here's the corresponding Git repository: sk22/spg-tinf-sem03/proj01

Thanks in advance!

Edit: I want to be able to import the scss files, not the compiled css.

like image 232
sk22 Avatar asked Dec 28 '16 19:12

sk22


People also ask

Does Vue use PostCSS?

PostCSS. Vue CLI uses PostCSS internally. You can configure PostCSS via . postcssrc or any config source supported by postcss-load-config, and configure postcss-loader via css.

How do I add SCSS to Vue 3?

Install vue 3 If you have the Vue CLI tool, just run vue create scss and select Vue 3. There is an option to set up CSS pre processors from the CLI tool but for this article I will ignore that option. Once set up, cd scss to move into that directory.


2 Answers

EDIT: Webpack has a section on sass-loader now: https://webpack.js.org/loaders/sass-loader/ also mentioning includepaths.

I had the same issue with @material and Vue. I managed to resolve the problem without adjusting the use property directly.

Solution

Step 1: First create a default Vue 2.1 project using the CLI. Your file structure will have a ./build directory.

Step 2: Open the file 'utils' you will see a cssLoaders() function which returns an object/map for the languages vue-loader supports.

You will see both sass and scss in that map.

Step 3: Change the values of sass and scss to:

  sass:    generateLoaders('sass', {
    indentedSyntax: true,
    includePaths:   [path.resolve(__dirname, '../node_modules')]
  }),
    scss:    generateLoaders('sass', {
  includePaths: [path.resolve(__dirname, '../node_modules')]
}),

Step 4: Go to the .vue file you're using and change the lang attribute in your <style> element to either sass or scss.

Step 5: After you've done that go to the terminal/console and install sass-loader with:

npm install sass-loader node-sass webpack --save-dev

Step 6: Then run npm run dev and it should work.

Why does this work?

Libraries

I dug around a bit and it turns out sass-loader uses node-sass which has some options such asincludePaths one mentioned by @22samuelk. IncludePaths tells node-sass or rather the underlying library LibSass to include sass files from that directory/path.

Vue

Sass-loader options

By default Vue expects your assets to be in your projects src/assets folder (correct me if I'm wrong). You can however use ~ to indicat you want to start at your projects root which would look like `~/node_modules/@material/smth/mdc-smth.scss.

Now if you want your sass-loader to use something other than those options you need to explicitly tell them.

Hence path.resolve(__dirname, '../node_modules' since the utils file is in ./build and you need to use an absolute path for sass-loader to understand where to look.

Vue-loader config

This is not really specific to the question but the vue-loader config defined in vue-loader.conf.js works as follows:

It uses the map returned by cssLoaders() to build the loaders expected by webpack. The returned map ({key:value}) is then used by providing key as a file extension used in test: for a loader object. The value is used as the loader object. Which would like like this:

{
  test: /\.(key)$/,
    use: [
  {
    loader: '//ld//-loader',
    options: {
      /*Options passed to generateLoaders('//ld//', options)*/
    },
  },
],
}

Where key is the file extention. In this case that would be either sass or scss. And //ld//is the loader you which to use. Which is shown in Step 3 as 'sass'.

Hopefully this clears up some stuff. Took me a while because I just started using Vue.

like image 32
Byebye Avatar answered Oct 23 '22 20:10

Byebye


Got it.

here's a part of my webpack 2 config's module.rules:

{
  test: /\.(sass|scss)$/,
  use: [
    'style-loader',
    'css-loader',
    {
      loader: 'sass-loader',
      options: {
        includePaths: [path.resolve(__dirname, 'node_modules')],
      },
    },
  ],
},

So what did I do wrong? My options object was placed in the rule directly, not the loader.

The old webpack config rule looked like this:

{
  test: /\.(sass|scss)$/,
  use: ['style-loader', 'css-loader', 'sass-loader'],
  options: { includePaths: [path.resolve(__dirname, './node_modules')] },
},

See the difference? Instead of the 'sass-loader' string, I extended it to an object, containing the loader name and the options object, because the options only apply to the sass-loader.

(You could also drop the path.resolve and only write 'node_modules', but it might be safer to leave it.)

Check out this documentation page for further information. https://webpack.js.org/configuration/module/#rule-use

Without that loader, you must prefix each import with a ~, which webpack converts to the node_modules folder, at least with my previous configuration. But this will break 3rd party SCSS frameworks like Material Components Web, because they use @import statements without a leading ~ themselves, for example here.

Inside .vue files

This will not work in .vue files, as vue-loader just uses sass-loader without any options by default. So if you want that to work, you probably need to make use of vue-loader's own options, as described in its documentation.

(I'm unable to get it to work for some reason I don't know...)

like image 59
sk22 Avatar answered Oct 23 '22 20:10

sk22