Webpacker: Cannot import modules with non-relative imports

Created on 1 Jun 2017  ยท  7Comments  ยท  Source: rails/webpacker

I'm trying to use Material Components Web (MDC-Web) but with no success.

The given example in webpacker README works for single file with non-relative imports within like:

@import '~@material/animation/mdc-animation.scss' // works fine

However it fails when I try to import whole MDC as suggested in https://yarnpkg.com/en/package/material-components-web

```(bash)
// app-styles.sass
@import "~material-components-web/material-components-web"

// console
Module build failed:
@import "@material/animation/mdc-animation";
^
File to import not found or unreadable: @material/animation/mdc-animation.
Parent style sheet: /project_root/node_modules/material-components-web/material-components-web.scss
in /project_root/node_modules/material-components-web/material-components-web.scss (line 17, column 1)

In /project_root/node_modules/material-components-web/material-components-web.scss (line 17, column 1)
`@import "@material/animation/mdc-animation";`

This doesn't seem to work whenever there is a non-relative import within the sass file being imported.

```[bash]
// app-styles.sass
@import "~@material/button/mdc-button"

// console
Module build failed:
    @import "@material/animation/variables";
    ^
          File to import not found or unreadable: @material/animation/variables.
    Parent style sheet: /project_root/node_modules/@material/button/mdc-button.scss
          in /project_root/node_modules/@material/button/mdc-button.scss (line 17, column 1)

In /project_root/node_modules/@material/button/mdc-button.scss (line 17, column 1)
@import "@material/animation/variables";

I tried tweaking things using below reources but couldn't figure it out.
https://webpack.js.org/configuration/resolve/
https://yarnpkg.com/en/package/module-alias

Related: https://stackoverflow.com/q/43187195

style loaders

Most helpful comment

Spent some time debugging and figuring out what going on in the webpacker code.
Here how I did it:

  • create a loader file as config/webpack/loaders/sass.js with content:
const cssLoader = require('@rails/webpacker/package/rules/css')
const cloneDeep = require('lodash/fp/cloneDeep')
const deepMerge = require('@rails/webpacker/package/utils/deep_merge')

let sassLoader = cloneDeep(cssLoader)
module.exports = deepMerge(sassLoader, {
  test: /\.(scss|sass)$/i,
  use: [{
    loader: 'sass-loader',
    options: {
      includePaths: ['node_modules'],
      sourceMap: true,
    },
  }]
})

The content is mostly copied from node_modules/@rails/webpacker/package/rules/sass.js
the important part is: includePaths: ['node_modules'],

  • import the loader in this file config/webpack/environment.js
    This is mine (working on a typescript project)
const { environment } = require('@rails/webpacker')
const typescript = require('./loaders/typescript')
const sass = require('./loaders/sass')

environment.loaders.append('typescript', typescript)
environment.loaders.append('sass', sass)

module.exports = environment

And it just works the hard part was inspecting webpacker code and figuring out what to do.

All 7 comments

Please use dist - @import "~material-components-web/dist/material-components-web"

I think there is a bug with library, those files are not there:

screen shot 2017-06-02 at 11 15 18

@gauravtiwari Those files are there within node_modules/@material along with node_modules/material-components-web directory.

(bash) โฏ ls node_modules | grep material @material material-components-web

material-components-web creates a separate folder named @material and stores various components within it as subfolders.

screen shot 2017-06-02 at 4 53 29 pm

@zoras Ahh that's why. You see, sass-loader needs a ~ in the beginning to understand that a particular file is using a node_modules import. Since material-components-web doesn't respect that unfortunately it's not possible - https://github.com/webpack-contrib/sass-loader#imports

The main file is referenced correctly but not the children modules. Another solution would be to import this inside .js file and see if that works?

import 'material-components-web/material-components-web'

@gauravtiwari nop, it didn't work and throws same error.

```(bash)
// in app/javascript/packs/application.js
import 'material-components-web/material-components-web'

// console
Module build failed:
@import "@material/animation/mdc-animation";
^
File to import not found or unreadable: @material/animation/mdc-animation.
Parent style sheet: stdin
in /project_root/node_modules/material-components-web/material-components-web.scss (line 17, column 1)
@ ./~/material-components-web/material-components-web.scss 4:14-201
@ ./app/javascript/packs/application.js

```

Is there any way to rewrite or tell sass-loader to check in ~ or node_modules directory by default?

@zoras Don't think that's easily possible. Have you figured it out yet? Perhaps you may want to open an issue on material-components-web repo.

Spent some time debugging and figuring out what going on in the webpacker code.
Here how I did it:

  • create a loader file as config/webpack/loaders/sass.js with content:
const cssLoader = require('@rails/webpacker/package/rules/css')
const cloneDeep = require('lodash/fp/cloneDeep')
const deepMerge = require('@rails/webpacker/package/utils/deep_merge')

let sassLoader = cloneDeep(cssLoader)
module.exports = deepMerge(sassLoader, {
  test: /\.(scss|sass)$/i,
  use: [{
    loader: 'sass-loader',
    options: {
      includePaths: ['node_modules'],
      sourceMap: true,
    },
  }]
})

The content is mostly copied from node_modules/@rails/webpacker/package/rules/sass.js
the important part is: includePaths: ['node_modules'],

  • import the loader in this file config/webpack/environment.js
    This is mine (working on a typescript project)
const { environment } = require('@rails/webpacker')
const typescript = require('./loaders/typescript')
const sass = require('./loaders/sass')

environment.loaders.append('typescript', typescript)
environment.loaders.append('sass', sass)

module.exports = environment

And it just works the hard part was inspecting webpacker code and figuring out what to do.

Finally figured this out and it's actually pretty simple. You need to update the existing default sass-loader!
simply add the following in config/webpack/enviroment.js just before module.exports

environment.loaders.get('sass').use.find(item => item.loader === 'sass-loader').options.includePaths = ['node_modules']

Which is actually shown pretty well in the documentation:

Probably a cleaner way to do it instead of chaining but it works.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ankitrg picture ankitrg  ยท  3Comments

ijdickinson picture ijdickinson  ยท  3Comments

ilrock picture ilrock  ยท  3Comments

Eearslya picture Eearslya  ยท  3Comments

naps62 picture naps62  ยท  3Comments