Webpack [url/file-loader] is not resolving the Relative Path of URL

11,357

Solution 1

So, finally after so much struggle, got a proper SOLUTION.

It turns out to be an issue with CSS-loader i.e it is not able to resolve the URL with respective to current file.

Using resolve-url-loader solved this problem. https://www.npmjs.com/package/resolve-url-loader

 // Old Loader Config in Webpack-entry
 loader: ExtractTextPlugin.extract('style-loader', 'css-loader?sourceMap!sass-loader?sourceMap')

 // New [Fixed] Loader Config in Webpack-entry
 loader: ExtractTextPlugin.extract('style-loader', 'css-loader?sourceMap!resolve-url-loader!sass-loader?sourceMap')

Here is updated Code-Repo with solution : https://github.com/raviroshan/webpack-build-issue

Note : Don't omit -loader Your Webpack.config.js should always use the long-form of the loader name (i.e. the -loader suffix).

There is another package called resolve-url which Webpack can confuse with resolve-url-loader.

Solution 2

It seems like it's css-loader fault and the way it resolves paths in @import and url(). It tries to resolve all paths — even those from imported stylesheets — relative to the main CSS file — which in your case is /Project_B/src/assets/stylesheets/index.scss.

Don't cry! There's a solution!

Maybe it's not perfect, but it's the best one I came with so far.

Create a global variable $assetsPath holding a path to assets relative to the current stylesheet. Then prepend this variable to all your url() values.

In your /Project_A/assets/stylesheets/index.scss you'd write:

/*/ Using !default we can override this variable even before the following line: /*/
$assetsPath: '../' !default;

.container {
    /*/ ... /*/
    .content-wrapper {
        /*/ ... /*/
        background-image: url($assetsPath + "images/content-bg.jpg");
    }
}

In your /Project_B/src/assets/stylesheets/index.scss you'd write:

/*/ The following variable will override $assetsPath defined in the imported file: /*/
$assetsPath: '../../../../Project_A/assets/';

/*/ Import Project A SCSS [Common Varibles, Classes, Styling etc] /*/
@import "../../../../Project_A/assets/stylesheets/index";

The Aftermath

If you bundle Project-A with Gulp it's gonna see Project-A's code as:

        /*/ ... /*/
        background-image: url("../images/content-bg.jpg");

Although, when you bundle Project-B with Webpack it's gonna see Project-A's code as:

        /*/ ... /*/
        background-image: url("../../../../Project_A/assets/images/content-bg.jpg");

Therefore, you are saved.

Most definitely I'll look closer at this issue. All of this could be avoided if url-loader would respect a path in the @import statement and apply it to referenced assets accordingly. Either I'm missing something or it should be considered as a bug.

I hope you have a wonderful day!
~Wiktor

Solution 3

you need to set the publicPath as a relative path to get the relative path in file-loader.

Share:
11,357
Ravi Roshan
Author by

Ravi Roshan

Focused on JavaScript and Web ecosystem.. 🚀

Updated on June 07, 2022

Comments

  • Ravi Roshan
    Ravi Roshan almost 2 years

    I am facing a problem in Webpack regarding Relative Path. Let me try to explain the scenario :

    I have 2 separate project in Workspace directory :

    1. Project-A [Bundling using Gulp] : Stable & Working
    2. Project-B [Bundling using Webpack] : New project

    As both the projects are using same Styling, so I wanted to reuse the SCSS files [consisting of standard variables, predefined layouts, modals, classes etc] of Project A into Project B.

    enter image description here

    Now, if I am trying to import Project-A index.scss in Project-B index.scss as another partial [Commenting out the Background Image URL Depency], webpack is able to generate the required CSS output file.

    // Import Project A SCSS [Common Varibles, Classes, Styling etc] 
    @import "../../../../Project_A/assets/stylesheets/index";
    

    But as Project-A's index.scss is further referring background images from the respective Relative-Path, the webpack build is throwing error

    'File / dir not found in XYZ/Project-B/Source/Stylesheets'.

    Exact Error Block :

    ERROR in ./src/assets/stylesheets/index.scss Module build failed: ModuleNotFoundError: Module not found: Error: Cannot resolve 'file' or 'diWorkSpace\Project_B\src\assets\stylesheets

    screenshot : **enter image description here**

    I am not able to understand, why Webpack is not able to resolve the Relative path of assets inside Project-A and still looking inside 'Project B'.

    Here is the Code-Repo URL for the simulated issue : https://github.com/raviroshan/webpack-build-issue/tree/master/WorkSpace

    Steps to reproduce.

    1. Download the Repo.
    2. Browse inside Project_B folder, and do a NPM install.
    3. Run 'webpack'. It would build correctly as Relative Image URL code is commented out.
    4. Now put back the commented line of code : https://github.com/raviroshan/webpack-build-issue/blob/master/WorkSpace/Project_A/assets/stylesheets/index.scss#L27
  • Ravi Roshan
    Ravi Roshan over 7 years
    So, my current code for public path is : publicPath: '/public/' Even if I am trying to change it to any relative path, I am getting the SAME error. publicPath: '.../../public/' Could you please elaborate a bit or am I missing something ?
  • Ravi Roshan
    Ravi Roshan over 7 years
    I tried referring to github.com/webpack/file-loader/issues/46#issuecomment-258656‌​602 "The core of the problem is that CSS loads assets relative to itself, and js loads assets relative to the HTML. So if the CSS isn't in the same place as the HTML then you can't use relative paths." So, looks like it won't work out. Any alternate ?
  • Khalid Azam
    Khalid Azam over 7 years
    try this name=[path][name].[ext] to the file-loader. { test: /\.jpg$/, loader: "file-loader?name=[path][name].[ext]" }
  • Ravi Roshan
    Ravi Roshan over 7 years
    This is not working. I guess the solution what you are providing is for linking the URL to bundle.css. My issue is - I am not able to build the Bundle itself. Will be updating my Question with sample Codebase URL.
  • Ravi Roshan
    Ravi Roshan over 7 years
    Your approach will work out, but I don't wanna make any change in the Project_A Code base due to various reasons. I have found a easier solution, posting it here...
  • Olav Kokovkin
    Olav Kokovkin almost 7 years
    Thanks a lot! In my case i couldn't get publicPath to clear out the config.publicPath part. Setting it to "./" worked
  • JonathanPeel
    JonathanPeel almost 7 years
    I am also trying to get this working. I am exporting CSS to a folder called Content. Then images use the 'Content/[hash].[ext]'. This adds images to ~/Content, but when the CSS looks for the images it looks in Content/Content/....png