url() with variable or relative path in sass/scss is broken
See original GitHub issueThis issue organizes many issues around relative url() in sass/scss and adds many information.
(Please convert to discussion if it should be a discussion.)
Related: #6618, #3164, #2993, #5337, #4269
Premise
sass does not support rebasing relative url (https://github.com/sass/libsass/issues/532). For example,
/* src/foo.scss */
@import "./nested/bar.scss";
/* ---- */
/* src/nested/bar.scss */
.bar {
background: url('./bar.png'); /* intends nested/bar.png */
}
becomes
/* src/foo.css */
.bar {
background: url('./bar.png'); /* becomes src/bar.png */
}
Also there is no API for custom rebasing (https://github.com/sass/sass/issues/2535).
In sass-loader
, this feature is not implemented. It presents resolve-url-loader
for solution. This one rebases url after converted to css and uses sourcemaps to obtain the original file path.
Current Vite’s implementation
It is implemented by this rebaseUrls
function.
This function is called inside importer
option which is passed to sass.
But importer will only be called if it is not a relative import because of the resolve order.
Loads are resolved by trying, in order:
- Loading a file from disk relative to the file in which the @use or @import appeared.
- Each custom importer.
- Loading a file relative to the current working directory.
- Each load path in includePaths.
- Each load path specified in the SASS_PATH environment variable, which should be semicolon-separated on Windows and colon-separated elsewhere.
Which means if a file is resolved by relative path, rebaseUrls
functions won’t be called.
The example is below.
/* src/foo.scss */
@import "./nested/bar.scss";
/* @import "/@/nested/bar.scss"; */ /* if a alias is used `rebaseUrls` will be called */
/* ---- */
/* src/nested/bar.scss */
.bar {
background: url('./bar.png');
}
This is why #6618 only happened when alias is used. (So url won’t be rebased if it is imported by relative path.)
Also rebaseUrls
is doing the transform over sass files and this makes it unable to rebase url including variables like url($url)
(#3164, #2993, #5337).
This can be mitigated by not transforming url which starts with a variable (#4269).
But it won’t work if it is used like below.
$url: './relative.png';
.foo {
background: url($url);
}
Solutions
I suggest several solutions.
- Since this is not a sass’s official feature, drop this feature(rebasing relative path) from Vite.
- Supporting absolute paths (including paths from
config.root
) is easily achieveable by doingrebaseUrls
after the content is transformed to css.
- Supporting absolute paths (including paths from
- Mitigate as much as possible
- I have no idea how to call
rebaseUrls
even if it is imported by relative path.
- I have no idea how to call
- Implement a logic simillar to
resolve-url-loader
.- Maybe if the sourcemaps are enabled,
UrlRewritePostcssPlugin
can handle this.
- Maybe if the sourcemaps are enabled,
Issue Analytics
- State:
- Created a year ago
- Reactions:16
- Comments:7 (4 by maintainers)
Top GitHub Comments
@sapphi-red I was talking about this one:
However, upon trying to create a reproducer it does in fact seem to be working as expected now.
I suspect my issue is being caused by using Vite in a Symfony application with https://github.com/lhapaipai/vite-bundle
Apologies 😃
@andyexeter I see. It would be great if you create a new issue.