Cannot import stylesheets from node_modules without tilde (~)
Asked Answered
D

1

6

I'm trying to create a simple web app with material-components-vue and vue-cli with webpack, however, I found out that I cannot import stylesheets from node_modules without a preceding ~.

I have tried several webpack/vue-cli configs, and ended up with a config in vue.config.js passing loader options.

My vue.config.js looks like this:

module.exports = {
    css: {
      loaderOptions: {
        sass: {
          includePaths: [
              './node_modules', //here I include node_modules
            ]
        },
      }
    }
  }

So I expect to be able to import stuff like so:

@import 'normalize/normalize'

(assuming I have a directory called normalize in my node_modules which contains a file normalize.scss)

However, webpack throws an error, saying it cannot find the module.
But, this does work:

@import '~normalize/normalize'

This wouldn't be a problem if all @imports were written by me, but because I use a third-party module which has @imports inside them, webpack fails to compile.

EDIT 1:

As @Styx asked to

Share more configs, please

and

show the output of vue inspect --rule scss, and the whole file with this problematic import

Here it is:

My problematic file is pretty empty:

<template>
  <div id="app">
    <m-button>Hello</m-button>
  </div>
</template>

<script>
import Vue from 'vue'
import Button from 'material-components-vue/dist/button'
Vue.use(Button)

export default {
  name: 'App',
  components: {
}
</script>

<style lang="scss">
@import "~material-components-vue/dist/button/styles"; //this works
@import "material-components-vue/dist/button/styles"; //but this does not
</style>

My output from vue inspect --rule scss is located here

All other configs are as generated by vue init webpack <name>

EDIT 2: Exact steps to reproduce this issue:

  1. Initialize a vue-webpack app:
vue init webpack .
  1. Vue build: Runtime + Compiler (Default)
  2. Vue-router: no
  3. Package manager: npm

  4. Then, install sass-loader

npm i -D sass-loader node-sass
  1. Create a file vue.config.js and populate it with the following:
module.exports = {
    css: {
      loaderOptions: {
        sass: {
          includePaths: [
              './node_modules', //here I include node_modules
            ]
        },
      }
    }
  }
  1. After that, install a module containing scss/sass (E.g. for material-components-web, npm i material-components-web)

  2. Then, create an import to a stylesheet located in node_modules, like so:

@import '@material/button/mdc-button'; //mdc-button comes with material-components-web
  1. Finally, start the dev server:
npm run dev
  1. It will throw the following error:
 ERROR  Failed to compile with 1 errors                                                                        11:36:35 AM

 error  in ./src/App.vue

Module build failed: 
@import '@material/button/mdc-button';
^
      File to import not found or unreadable: @material/button/mdc-button.
      in /home/maxim/projects/holiday.js/stackoverflow/src/App.vue (line 18, column 1)

 @ ./node_modules/vue-style-loader!./node_modules/css-loader?{"sourceMap":true}!./node_modules/vue-loader/lib/style-compil
er?{"vue":true,"id":"data-v-7ba5bd90","scoped":false,"hasInlineConfig":false}!./node_modules/sass-loader/lib/loader.js?{"s
ourceMap":true}!./node_modules/vue-loader/lib/selector.js?type=styles&index=0!./src/App.vue 4:14-359 13:3-17:5 14:22-367
 @ ./src/App.vue
 @ ./src/main.js
 @ multi (webpack)-dev-server/client?http://localhost:8080 webpack/hot/dev-server ./src/main.js

By the way, in the first example I wanted to import material-components-vue/dist/foo/styles, but here I import @material/foo.

Defalcate answered 1/5, 2019 at 8:58 Comment(5)
Can't reproduce. Share more configs, please.Overhasty
Specifically, show the output of vue inspect --rule scss, and the whole file with this problematic import.Overhasty
@Styx, thanks for asking! I'm pretty new to webpack, so if any other info is needed, please ask!Defalcate
Still can't reproduce, it works for me. Show the whole error text, please.Overhasty
@Styx, I posted the steps + the error text above, hope it will help.Defalcate
O
2

In this configuration your vue.config.js is ignored. This file is used by @vue/cli-service, but you're using webpack-dev-server instead. Thus, your sass-loader doesn't receive this includePaths option.

You can either use modern vue create <app-name> command, or if you want to modify existing project:

  1. Open build/utils.js file.

  2. Find return ... in exports.cssLoaders function:

    return {
      ...
      sass: generateLoaders('sass', { indentedSyntax: true }),
      scss: generateLoaders('sass'),
      ...
    }
    
  3. Modify it like this:

    const includePaths = [path.resolve(__dirname, '..', 'node_modules')];
    return {
      ...
      sass: generateLoaders('sass', { indentedSyntax: true, includePaths }),
      scss: generateLoaders('sass', { includePaths }),
      ...
    }
    
  4. Remove unused vue.config.js file.

Overhasty answered 3/5, 2019 at 12:13 Comment(2)
Hi. This helped me a lot already, but I have a follow-up question: I am under the impression that once I set includePaths as you did, my main.js cannot find a style file that is not in node_modules (typically in src/assets). Do you know if it is possible to include multiple paths with this method? ThanksSpectacular
@Spectacular yes, it’s possible: includePaths in an array of paths, you can add as many items as you need there. However, these paths don’t disable normal path resolution, so you’ll still be able to use normal imports as usual.Overhasty

© 2022 - 2024 — McMap. All rights reserved.