less-loader: Unable to resolve module error and reproduction of issue

I’ve run into a bunch of issues attempting to use @import and url() when adding CSS modules to a current project and using less-loader.

It looks like a lot of people have this error:

… and so on.

I absolutely cannot get any kind of relative file load to work. I’ve attempted to create a minimal reproducible test case for the issue. The repository is located here: https://github.com/jsonnull/less-modules-resolve.

npm install and npm run build to see the issue.

ERROR in ./~/css-loader?modules!./~/less-loader?sourceMap!./src/index.less
Module not found: Error: Can't resolve 'material-design-icons/MaterialIcons-Regular.ttf' in '/Users/jsonnull/less-modules-resolve/src'
 @ ./~/css-loader?modules!./~/less-loader?sourceMap!./src/index.less 6:470-528
 @ ./src/index.less
 @ ./src/index.js

See below, the file is clearly present:

├── dist
│   └── build.js
├── npm-debug.log
├── package.json
├── src
│   ├── index.js
│   ├── index.less
│   └── material-design-icons
│       ├── LICENSE
│       ├── MaterialIcons-Regular.eot
│       ├── MaterialIcons-Regular.ijmap
│       ├── MaterialIcons-Regular.svg
│       ├── MaterialIcons-Regular.ttf
│       ├── MaterialIcons-Regular.woff
│       ├── MaterialIcons-Regular.woff2
│       ├── README.md
│       ├── codepoints
│       └── material-icons.less
└── webpack.config.js

I’ve tried every combination for the less file import as well as the url() import I can think of…

  • file - I think this one should work?
  • ./file - I think this one should definitely work
  • ~file - I think this one should not work
  • /file - I think this one should not work

And, anyways, none of these methods work.

Assuming this is possible, how can the repository above be fixed so that all issues are resolved using the current directory structure? Otherwise, are there really combinations of relative paths that are completely impossible to use for importing?

From scrounging through issues it really does seem like nobody has a clear understanding of what this issue is or how to fix it. If we can come up with a clear answer for this issue for why it doesn’t work and how to fix, hopefully others can learn from this and we can come up with good documentation for how to avoid this issue.

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Reactions: 7
  • Comments: 18 (2 by maintainers)

Most upvoted comments

For those that come across this issue and find that it completely blocks them from using less-loader… for example I have styling from some 3rd party libs that have url( references to files that don’t exist on disk, like chosen-bootstrap references images that ship with chosen but aren’t included in chosen-bootstrap itself.

You can disable all this URL mangling with relativeUrls: false on less-loader, and url: false on css-loader.

      {
        test: /\.less/,
        use: [{
          loader: 'style-loader'
        }, {
          loader: 'css-loader',
          options: {url: false, sourceMap: true}
        }, {
          loader: 'less-loader',
          options: {relativeUrls: false, sourceMap: true}
        }]
      },

This makes all url() entries stay as they were in the original less and css source (unchanged) and webpack will no longer try to resolve them. This also means that you no longer need file-loader or url-loader to process each of those files, however you also need a build step to copy the static assets into your own deploy directory where the web server can get to them. It also means there will be hot-reloading for the less files, but not for fonts and images.

This was fine for me, as I was mostly just wanted the less file hot reloading.

Thx for the detailed bug report. With your description and the minimal test example I was able to spot the issue very quickly 👍

Unfortunately, that’s a really tricky one, let me explain it:

  • First, webpack encounters the index.less and passes it to less-loader
  • The less-loader passes the file to less
  • Less parses the file and discovers the @import statement. Since nested imports are legal less, less is resolving the material-icons.less with the help of the less-loader
  • Now less has all the less files and compiles them down to CSS
  • During this compile step, less rewrites all urls so that the output contains the correct path. This is because the less-loader sets the relativeUrls option. This option is very useful, since it allows you to write your less files in a modular way, just reference your images and the final CSS contains valid urls. The urls look like this:
// before compilation
url('MaterialIcons-Regular.eot')
// after compilation
url('material-design-icons/MaterialIcons-Regular.eot')
// all urls are relative to index.less
  • This CSS is now passed to the css-loader with the modules option enabled. If a CSS module contains an import statement that does not start with a dot, it is considered to be a module request and webpack will search in node_modules for it.
  • If less had rewritten the url to url('./material-design-icons/MaterialIcons-Regular.eot') everything would work just fine.

It basically boils down to LESS + relativeUrls: true + CSS modules is not compatible. Either you convince less to rewrite their urls so that relative urls start with a dot, or you override the relativeUrls option in the less-loader or you get rid of less at all (I think, in combination with CSS modules less is not really necessary).

👍 please resolve that issue @jhnns

For future reference: passing {rewriteUrls: "local"} as Less option should fix the problem. Check out the Less documentation for more information.

It is fine. 🤔

.loader{
   background-image: url("\./301.gif");
}

@jsonnull I found that if wrap the less file with a folder and prepend ‘…/’ when using url() can work. files distributes like below:

[module_root]/ ----index.js (Here, use import ‘./css/index.less’) ----css/ --------index.less (Here, use url(‘…/assets/test.eot’)) ----assets/ --------test.eot

It looks so ugly, but i work.