Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to resolve module error and reproduction of issue #109

Closed
jsonnull opened this issue Sep 25, 2016 · 18 comments
Closed

Unable to resolve module error and reproduction of issue #109

jsonnull opened this issue Sep 25, 2016 · 18 comments

Comments

@jsonnull
Copy link

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.

@tamer1an
Copy link

tamer1an commented Oct 14, 2016

👍 please resolve that issue
@jhnns

@jhnns
Copy link
Member

jhnns commented Oct 14, 2016

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).

@jsonnull
Copy link
Author

@jhnns Thanks for looking into this!

I'm going to close this issue, but I've left the repository in place in case anybody else comes across this issue.

@m19c
Copy link

m19c commented May 10, 2017

+1 - same problem :-/

@jsonnull
Copy link
Author

@MrBoolean unfortunately this problem is not going away any time soon. As stated above "It basically boils down to LESS + relativeUrls: true + CSS modules is not compatible"

I migrated away from LESS because I needed CSS modules and relative imports, and CSS formats are relatively interchangeable these days.

@chardlau
Copy link

chardlau commented May 25, 2017

@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.

@txiaocao
Copy link

txiaocao commented Sep 21, 2017

Haha, I resolve it; this is hack
I create images.css,and require the css files,and use
the code

private images = require("./images");
this.images.loader
.loader{
background-image: url("./301.gif");
}

@limichange
Copy link

limichange commented Jan 8, 2018

It is fine. 🤔

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

@rally25rs
Copy link

rally25rs commented Jan 15, 2018

During this compile step, less rewrites all urls so that the output contains the correct path.

@jhnns is there a way to disable this? When importing a lot of 3rd party CSS files, they have url()s set to where the file will be hosted not where it is on disk. These may not be resolvable by any loader, and the files can't be edited (they come from other dependencies).

css-loader can be passed url: false to stop processing those URLs, but less-loader always changes them, making them incorrect at runtime.

@rally25rs
Copy link

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.

@garygreen
Copy link

garygreen commented Feb 20, 2018

@rally25rs thanks so much for that - it's worth noting that if you only need to do this for certain files you can change the rule so it only uses that loader with those custom options if needed. I'm a total noob when it comes to Webpack, so not sure if I'm doing it right but it seems to be working good for me.

Here's my current webpack config:

       {
           test: /\.less$/,
           exclude: /site.less$/,
           use: ExtractTextPlugin.extract({
               use: ['css-loader', 'less-loader'],
               fallback: 'style-loader'
           })
       },
       // Custom less compiler for frontend site less.
       // This basically just turns off resolving of url() as they should all be in the public_html already
       // so don't need to be resolved, and would cause problems if they are.
       {
           test: /site.less$/,
           use: ExtractTextPlugin.extract({
               use: [{
                   loader: 'css-loader',
                   options: {url: false, sourceMap: true}
               }, {
                   loader: 'less-loader',
                   options: {relativeUrls: false, sourceMap: true}
               }],
               fallback: 'style-loader'
           })
       },

@mqliutie
Copy link

mqliutie commented May 30, 2018

@garygreen Thanks for your codes

@boylec
Copy link

boylec commented Oct 3, 2018

I got this to work without needing to turn off relativeUrls by making use of 'url-loader' for fonts/images:

url-loader converts files (fonts and images in this case) into base-64 strings. This way the resulting css references for fonts and/or images is a direct base64 string representation of the image or font instead of a path (relative or otherwise).

For example:

{
    test: /\.(woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/,
    enforce: "pre", // preload the jshint loader
    use: [{
        loader: "url-loader",
        options: {
            query: { limit: 25000 }
        }
    }]
},
{
    test: /\.(png|jpg|jpeg|gif|svg)$/,
    enforce: "pre", // preload the jshint loader
    exclude: /node_modules/, // exclude any and all files in the node_modules folder
    use: [{
        loader: "url-loader",
        options: {
            query: { limit: 25000 }
        }
    }]
}

You could probably combine the loaders but I wanted them to behave slightly differently - ie exclude: /node_modules/.

@matthew-dean
Copy link

matthew-dean commented Nov 21, 2018

Does the --rewrite-urls=local option resolve this issue? http://lesscss.org/usage/#less-options-rewrite-urls

Considering that @jhnns added the Less functionality, I think it addressed this issue, correct? If so, it would make sense to remove the "CSS Modules gotcha" in the readme.

Note the original issue wasn't so much even Less + relativeUrls: true (now rewriteUrls: 'all') + CSS Modules, but actually Less + relativeUrls: true + CSS modules + the Less plugin + webpack loader behavior working together to cause a problem, but the additional option should make this more flexible and compatible with Webpack + CSS modules.

@matthew-dean
Copy link

Also, as noted in #276, no special ~ syntax should be needed to load styles from the node_modules folder. Less-loader is actually breaking Less module-loading functionality, but maybe that's by design. (I'm not a Webpack expert.)

@jhnns
Copy link
Member

jhnns commented Apr 25, 2019

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

@manugb
Copy link

manugb commented Nov 1, 2019

For remote @import urls check this out
less/less.js#3188 (comment)

@kanishka-malhotra
Copy link

Following configuration helped me resolve the issue with relative URLs due to less-loader + CSS modules (mentioned here)

{
  test: /\.less$/,
  use: [
    {
      loader: MiniCssExtractPlugin.loader,
      options: {
        publicPath: '/'  // NOTE: this option does the charm to add `/` in front of all relative assets (don't use `./`)
      }
    },
    {
      loader: 'css-loader',
      options: {
        importLoaders: 2,
      },
    },
    {
      loader: 'postcss-loader',
      options: {
        postcssOptions: {
          config: path.resolve(__dirname, 'postcss.config.js'),
        },
      },
    },
    {
      loader: 'less-loader',
    },
  ],
}

Hope this helps!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests