bholloway / resolve-url-loader

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

3.0.0 Breaks Relative Paths in CSS #112

Closed epihel closed 5 years ago

epihel commented 5 years ago

In version 2.3.1, fonts and images with relative paths referenced in CSS files work fine. But in 3.0.0, if the path doesn't begin with /, resolve-url-loader inserts an ./includes/ directory before the path and produces the following error:

ERROR in ./src/css/main.css (./node_modules/css-loader/dist/cjs.js!./node_modules/resolve-url-loader!./node_modules/sass-loader/lib/loader.js?sourceMap!./src/css/main.css)
Module not found: Error: Can't resolve './includes/public/fonts/myfont.otf' in '/Users/dev/src/css'
 @ ./src/css/main.css (./node_modules/css-loader/dist/cjs.js!./node_modules/resolve-url-loader!./node_modules/sass-loader/lib/loader.js?sourceMap!./src/css/main.css) 9:41-106
 @ ./src/css/main.css
 @ ./src/index.js
 @ multi (webpack)-dev-server/client?http://localhost:3100 (webpack)/hot/dev-server.js ./src/index

The webpack config:

{
    test: /\.(css|scss)$/,
    loaders: ['style-loader', 'css-loader', 'resolve-url-loader', 'sass-loader?sourceMap']
}

The font declaration:

@font-face {
    font-family: "My Font";
    src: url("public/fonts/myfont.ttf") format('truetype'),
        url("public/fonts/myfont.otf") format('opentype');
}

The relevant dependencies/versions from package.json:

"css-loader": "2.1.0",
"react-hot-loader": "4.6.3",
"resolve-url-loader": "3.0.0",
"sass-loader": "7.1.0",
"style-loader": "0.23.1",
"webpack": "4.29.0",
"webpack-dev-server": "3.1.14"
bholloway commented 5 years ago

That’s strange. I’m guessing there is no “includes” directory in you project file system.

This is not some special string in “resolve-URL-loader”.

Try the debug option in the loader to see what paths it is processing.

epihel commented 5 years ago

Turning on debugging shows many lines with ./src/css/includes NOT FOUND. We have SASS files such as src/css/includes/main.scss and these have relative references to fonts and images. But instead of starting at the compiled CSS directory, it looks like resolve-url-loader starts inside the SASS source includes directory. Is there a way to tell it to start in the compiled CSS files? It works in 2.3.1.

bholloway commented 5 years ago

Version 2 did a full file system search which has been discontinued.

It’s most likely your build relied on that.

It seems that the sass sourcemap attributes the URL statement to the includes folder. Do you have mixins there that write the URL?

bholloway commented 5 years ago

Sorry I misread. You have already confirmed that this is the case.

The entire aim of this loader is to achieve the path relative to the original SCSS source file.

If you want relative to the complied CSS then simply remove this loader. Because the css loader already does what you want without further URL rewriting.

epihel commented 5 years ago

No; I don't have mixins that write the URL. Without resolve-url-loader, none of the relative font or image paths in the SCSS files are resolved. But I haven't been able to figure out any way to get this to work in 3.0.0. I don't think I'm doing anything unusual. My loaders are:

{ test: /\.(css|scss)$/, use: [
    { loader: 'style-loader' },
    { loader: 'css-loader' },
    { loader: 'resolve-url-loader', options: { debug: true } },
    { loader: 'sass-loader', options: { sourceMap: true, sourceMapContents: false } }
] }
bholloway commented 5 years ago

Can you give some file paths relative to your project root.

Specifically

epihel commented 5 years ago

src/index.js

import 'css/main.css';

src/css/includes/main.scss

div {
    background-image: url(public/img/arrow-left.svg);
}

src/css/main.css

@import "includes/main";

src/public/img/arrow-left.svg

<?xml version="1.0" encoding="UTF-8"?>
<svg width="33px" height="21px" viewBox="0 0 33 21" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <g fill="#000000">
        <path d="M30.8157126,6.89910508 L13.4472916,6.89910508 L14.3924811,1.30420678 C14.3924811,1.04437627 14.2893126,0.802342373 14.1152811,0.624376271 C14.0465021,0.552122034 13.9669547,0.494461017 13.8790705,0.447833898 C13.5629653,0.281257627 13.1860705,0.310088136 12.9026179,0.523291525 L0.549154737,9.90388475 C0.303217895,10.0854102 0.159407368,10.3754949 0.162533684,10.6883593 C0.163923158,10.9432068 0.263965263,11.1838169 0.437996842,11.3617831 C0.475512632,11.4002237 0.517544211,11.4372407 0.563049474,11.4689186 L12.9356179,20.4985627 C13.2232389,20.7053593 13.5973547,20.7309864 13.9089442,20.5676136 C14.2187968,20.4006814 14.4126284,20.0721559 14.4112389,19.7144441 L13.4535442,14.1149186 L30.8157126,14.1149186"></path>
        <path d="M30.8157126,14.1149186 C32.7609758,14.1149186 32.7717442,12.5000542 32.7717442,10.5068339 C32.7717442,8.51396949 32.7609758,6.89910508 30.8157126,6.89910508"></path>
    </g>
</svg>
bholloway commented 5 years ago

@epihel it seems likely you have your assets in public/. In this case you have a couple of options.

If you go with the latter option you might need to map all the url() statements to your project root directory. Probably you would use the webpack resolve config to do this.

If you have to rebase the url() paths somewhere along the way keep in mind postcss-loader with the postcss-url plugin. This used to be possible with postcss-loader using the root option but they remove it in this release.

Keep in mind that resolve-url-loader is for the case where you co-locate your assets with your scss files in a feature-based structure. It will not work here. You will need to remove it.

epihel commented 5 years ago

Thanks for the tips. I was able to get it to work by removing resolve-url-loader, adding a tilde before all my font and image references (e.g., ~public/fonts/VC1/VC1-Regular.otf), and adding the following to my webpack config:

resolve: {
    alias: {
        public: path.join(__dirname, 'src', 'public')
    }
}