bholloway / resolve-url-loader

Webpack loader that resolves relative paths in url() statements based on the original source file
563 stars 71 forks source link

v3.0.0 broke module resolution #105

Closed vinnymac closed 5 years ago

vinnymac commented 5 years ago

Summary

After upgrading from 2.3.1 to 3.0.0 I was unable to build my app successfully (no other changes were made). This is a closed source project otherwise I would link to the source.

I saw many errors that look identical to the following printed out with stack traces after upgrading and building.

Module not found: Error: Can't resolve './x.png' in 'SOME_ABSOLUTE_PATH'

x.png in my sass will looks something like this

.myclass
  background: url('x.png') center center no-repeat

I am using style-loader, css-loader, resolve-url-loader, postcss-loader, and sass-loader (in that order). The configuration for resolve-url-loader looks like this

{
  loader: 'resolve-url-loader',
  options: {
    sourceMap: true,
    root: path.resolve('./'),
  },
}

My images exist adjacent to my styles in a folder structure that looks something like

/
  /assets
    /images
      x.png
    /styles
      index.sass

I've used this library since 2015 without any errors, so I was a little surprised when I ran into this. I read the Release Changelog ahead of time, and also gave removing the root configuration a try. That didn't help so I tried to use join to get it working, but was not able to. Perhaps I should be trying to solve this some other way, but I would rather continue using this as I was. Let me know if you need any more info. Thanks.

Versions

Webpack: v4.20.2 Node: v8.11.1 NPM: v5.6.0 OS: macOS 10.14 Resolve-url-loader: v3.0.0

bholloway commented 5 years ago

@vinnymac good to hear from a long-term user. Definitely hoping we can solve this issue.

I suspect your build relied on a full file search which is now removed since 3.0.0. But certainly I want to work through this systematically before reaching that conclusion.

Note: I am going to use ~ to refer to your project directory

Here are my assumptions.

This would mean...

Please confirm or refute before we move on.

vinnymac commented 5 years ago

@bholloway thanks so much for replying so quickly!

I can confirm that all these assumptions are correct and everything has always been structured this way since this project was started.

bholloway commented 5 years ago

@vinnymac no problem. ~Maybe we are fortunate enough to be on the same time-zone for once! 😉~ (haha I was wrong NY !== AU)

So I think my initial suspicions were correct.

Some history

When this loader was originally created it was common for dependencies to not resolve in the npm way. Webpack was in its infancy and non-js assets were distributed with bower.

Even in npm packages it was common for sass packages, such as bootstrap, to include assets in some ways that were hard to generalise. Frustratingly they tended to be almost relative to their point of use. For example url(fonts/some-font.woff) might be located in ../fonts/some-font.woff which is pretty close.

Using a file search both shallower and deeper in the directory tree the file could usually be found.

However...

From what I see you are caught by this last point and I do apologise.

Version 3.0.0

In retrospect the file search "magic" was a bad idea and should have been removed long ago.

However it was only recently that we have enough automated tests to ensure major changes such as this could be made.

For the use-case where the asset needs to be resolved relative to the original source file then a custom join option may be required. However I believe this is not your case.

In your case you have all your assets located in a directory known apriori. In that case you probably don't need resolve-url-loader and your build will be faster as a result.

The optimal solution for you

This is where we might need further discussion. Here are some ideas...

In some ways I think postcss is the better option. The reason being that it is more targeted. You know that it is only going operate on the files that enter that particular loader chain.

@vinnymac in short - the feature that you have been using is no longer present. I understand that this is a breaking change and is inconvenient for your existing project. However I do believe that you will have a more performant, stable build if you can make this change.

In general I advise you to diff in your build output before/after upgrade to prove that it is a safe change.

vinnymac commented 5 years ago

@bholloway thanks for the great breakdown it is much appreciated!

I expected this may be the case when I was going through this issue yesterday. I began looking at PostCSS options, but did not find anything I liked. I attempted the resolve.alias solution you'll find referenced in obscure places like Github Issues and StackOverflow but not in the official docs for css-loader. However I got it working within a few minutes by doing this.

  1. Add an alias for your images folder
  2. Reference this using url('~images/x.png') everywhere you need to
  3. Test webpack builds successfully

The only downside to this solution is that it required me to rewrite every single url in all of my css/scss/sass that I have loading so they were correctly pointing at the necessary paths.

Here is what I had to add to the root resolve object in my webpack config.

alias: {
  images: path.resolve(__dirname, 'assets/images'),
},

I consider this solved, but I wanted to thank @bholloway for actually taking the time to discuss this with me, most maintainers would have just shut this down and said to move to stack overflow or something similar. Fortunately you were willing to talk it through and helped me head in the right direction. I can only hope that this issue will come up in future searches when other people in my situation have the same problem.

Thanks