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

3

12

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
Mispronounce answered 23/12, 2016 at 19:18 Comment(1)
I added <base href="/the/root/folder/" /> to my public/index.html to fix a similar issue (I like that solution better than adding yet another npm package)Zootomy
M
10

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.

Mispronounce answered 20/1, 2017 at 7:29 Comment(1)
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/....pngCommotion
H
3

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

Halland answered 10/1, 2017 at 14:47 Comment(1)
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...Mispronounce
A
2

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

Aarhus answered 26/12, 2016 at 20:49 Comment(5)
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 ?Mispronounce
I tried referring to github.com/webpack/file-loader/issues/46#issuecomment-258656602 "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 ?Mispronounce
try this name=[path][name].[ext] to the file-loader. { test: /\.jpg$/, loader: "file-loader?name=[path][name].[ext]" }Aarhus
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.Mispronounce
Thanks a lot! In my case i couldn't get publicPath to clear out the config.publicPath part. Setting it to "./" workedRiegel

© 2022 - 2024 — McMap. All rights reserved.