Next.js: Sass loader cannot resolve modules from Yarn v2 PNP

Created on 31 Jul 2020  Â·  17Comments  Â·  Source: vercel/next.js

Bug report

Describe the bug

Next.js does not resolve SCSS that is imported by my dependencies:

  1. styles/home.module.scss uses @material/elevation (this works fine).
  2. @material/elevation uses @material/animation (this fails).

To Reproduce

Steps to reproduce the behavior, please provide code snippets or a repository:

  1. First, clone this repository and CD inside:
$ git clone https://github.com/nicholaschiang/with-sass
$ cd with-sass/
  1. Then, make sure you have Yarn installed then run:
$ yarn
  1. Finally, start the development server by running:
$ yarn dev

Error

You'll notice that when you run yarn dev the Next.js server starts up properly
but the sass-loader is unable to resolve my dependency's imports.

It seems to import the @material/elevation package just fine:

// styles/home.module.scss
@use '@material/elevation

But it doesn't seem to be able to import @material/animation for the
@material/elevation package:

error - ./styles/home.module.scss (./.yarn/$$virtual/css-loader-virtual-393ee7c517/0/cache/css-loader-npm-3.5.3-0f886851e6-910936f0ac.zip/node_modules/css-loader/dist/cjs.js??ref--5-oneOf-3-1!./.yarn/$$virtual/next-virtual-4df8b3b97e/0/cache/next-npm-9.5.1-e14f31e6e9-effa9056b8.zip/node_modules/next/dist/compiled/postcss-loader??__nextjs_postcss!./.yarn/cache/resolve-url-loader-npm-3.1.1-cf1a268137-7b113ac9e6.zip/node_modules/resolve-url-loader??ref--5-oneOf-3-3!./.yarn/$$virtual/sass-loader-virtual-dbafe3ebab/0/cache/sass-loader-npm-8.0.2-f0d209ad64-e23d9b308f.zip/node_modules/sass-loader/dist/cjs.js??ref--5-oneOf-3-4!./styles/home.module.scss)
SassError: Can't find stylesheet to import.
   â•·
23 │ @use "@material/animation/variables";
   │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   ╵
  .yarn/cache/@material-elevation-npm-7.0.0-aaa8862010-5ea26d080e.zip/node_modules/@material/elevation/_variables.scss 23:1  @forward
  .yarn/cache/@material-elevation-npm-7.0.0-aaa8862010-5ea26d080e.zip/node_modules/@material/elevation/_index.scss 1:1       @use
  /home/nchiang/repos/with-sass/styles/home.module.scss 1:1                                                                                                                  root stylesheet

I've had this error before when using Next.js with
MWC but was
able to fix it by specifying an includePaths configuration in next.config.js
like so:

// next.config.js
const path = require('path');

module.exports = {
  sassOptions: {
    includePaths: [
      path.resolve(__dirname, 'node_modules'),
    ],
  },
};

Now, I'm not sure how to tell sass-loader to use Yarn PNP's module resolution.

Expected behavior

Next.js should be able to resolve the Yarn PNP modules.

System information

  • OS: Ubuntu 18.04.2
  • Version of Next.js: 9.5.1
  • Version of Node.js: 14.7.0

All 17 comments

Seems like #6049 was closed rather prematurely.

Perhaps this could be fixed by using a custom importer: https://github.com/sass/node-sass#importer--v200---experimental

The issue is that sass-loader tries to load ./@material/animation/_variables.scss from @material/elevation/_variables.scss which doesn't work.
The relative path is produced here in 9.0.0 https://github.com/webpack-contrib/sass-loader/blob/c6d56e48728eb8d65258e6ee7606507187e0b457/src/utils.js#L223 and here in 8.0.2 https://github.com/webpack-contrib/sass-loader/blob/03773152760434a2dd845008c504a09c0eb3fd91/src/importsToResolve.js#L24

So it tries to look for these

[
  './@material/animation/_variables.scss',
  './@material/animation/_variables.sass',
  './@material/animation/_variables.css',
  './@material/animation/variables.scss',
  './@material/animation/variables.sass',
  './@material/animation/variables.css',
  './@material/animation/variables',
  '@material/animation/variables'
]

from C:\with-sass\.yarn\cache\@material-elevation-npm-7.0.0-aaa8862010-5ea26d080e.zip\node_modules\@material\elevation

@evilebottnawi is the module import @material/animation/variables supposed to become relative?

It seems as if the issue originates from loader-utils assuming that @material/animation/variables is a relative URL:

const url = "@material/animation/variables";
const request = loaderUtils.urlToRequest(url); // "./@material/animation/variables"

This is not the case. So either MWC changes all of their imports to prepend a ~ (which I don't think they'll do), or loader-utils has to stop assuming that all paths are by default relative (and instead assume that all paths are by default modules unless ./ is preprended).

Please do not use Yarn v2 PnP with webpack@4, only webpack@5 support PnP, also my strong recommendation - do not use includePaths and PNP, it is compatibility, because sass do not use require.resolve for @import and can't load virtual files :smile:

You can try https://github.com/arcanis/pnp-webpack-plugin, but I don't know real compatibility

@merceyz and yes and not https://github.com/webpack-contrib/sass-loader/blob/c6d56e48728eb8d65258e6ee7606507187e0b457/src/utils.js#L234, contains ['./@material/animation/variables', '@material/animation/variables'], because you can have a directory with @ character in name

@evilebottnawi I switched to Webpack v5 and I got the same error.

@evilebottnawi The PnP plugin is already installed, the import @material/animation/variables causes sass-loader to try to load this list:

[
  './@material/animation/_variables.scss',
  './@material/animation/_variables.sass',
  './@material/animation/_variables.css',
  './@material/animation/variables.scss',
  './@material/animation/variables.sass',
  './@material/animation/variables.css',
  './@material/animation/variables',
  '@material/animation/variables'
]

Would it make sense to also try these?

  '@material/animation/_variables.scss',
  '@material/animation/_variables.sass',
  '@material/animation/_variables.css',
  '@material/animation/variables.scss',
  '@material/animation/variables.sass',
  '@material/animation/variables.css',

Or is this behavior expected and therefor it's an issue with how @material/elevation/_variables.scss declares its import and @material/animation/variables should be changed to @material/animation/_variables.scss?

@merceyz that suggestion looks great! According to the Sass lang spec, the @material/animation/variables is definitely the correct way to import that partial (plus, that MWC package is maintained by Google, so I'm pretty sure they got their stuff right haha).

@nicholaschiang Are you sure what you uses latest version? Where you got list?

Anyway adding @material/animation/_variables make sense, anyway we need reproducible test repo with error? Can you open a new issue with minimum reproducible test repo with error?

@evilebottnawi there's already a repro linked in the original issue description (it's the default Next.js template with a SCSS import added).

And I was using Webpack 5.0.0-beta.22 originally (I can add it to the repro if you want too) by specifying Yarn resolutions (as per the instructions in #13341) when I first encountered this issue.

Thanks, I will look at this in near future

Just note - next.js uses sass-loader@8, but latest is sass-loader@9

Confirmed, it is bug, reproduced with sass-loader@9, WIP

Resolution in sass is terrible :disappointed:

Fixed, ETA is tomorrow, I found other strange resolution bug (very rare case), anyway you need to update sass-loader to latest, it will not work with sass-loader@8

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jesselee34 picture jesselee34  Â·  3Comments

YarivGilad picture YarivGilad  Â·  3Comments

timneutkens picture timneutkens  Â·  3Comments

renatorib picture renatorib  Â·  3Comments

irrigator picture irrigator  Â·  3Comments