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.
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.
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.
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.
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...)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With