Relative url() paths in compiled Sass files can be wrong
Suppose this setup:
/* assets/styles/app.scss */
import 'tools/base';
/* assets/styles/tools/base.scss */
.splash {
background-image: url('../../images/login-bg.png');
}
The image lives at assets/images/login-bg.png, so the relative path above is, correctly, relative to the source file. Currently, if you compile this, because the contents are all compiled into, basically, assets/styles/app.css, the relative path will now be incorrect.
Fix
I have 2 ideas to fix this:
- rails-sass look like they have you use some special fake functions in your scss files - https://github.com/rails/sass-rails#asset-helpers (the code, I think, actually lives in https://github.com/sass/sassc-rails). I'm not entirely sure how the implementation is done, but it looks like you literally do
asset-path("rails.png")in your CSS files and it's written.
Pros: looks simple to implement. Cons: this is magic, and your editor won't like it.
- When the final
.cssfile is built, we can also have it output an importmap (also, it would be great to expose that importmap - or perhaps "embed" it in the.cssfile for simplicity if possible in dev mode). We can, in theory, use this importmap to do this logic:
A) Determine that the background-image: url('../../images/login-bg.png'); line came from assets/styles/tools/base.scss
B) Determine that this file lives one directory deeper than the source, assets/styles/app.scss.
C) Adjust the path accordingly to ../images/login-bg.png'
This might be trickier to implement - I've never parsed a sourcemap for info - but would be a completely clean solution. This is also how resolve-url-loader from Webpack works - https://github.com/bholloway/resolve-url-loader/blob/HEAD/packages/resolve-url-loader/docs/how-it-works.md#the-solution
It could be simpler in fact: if you know that we should change just relatively, we can give that info as CLI option on compile time
https://sass-lang.com/documentation/cli/dart-sass/#source-map-urls
For the clean solution to work there would need to be some pipeline construction introduced to post process the generated css (and its sourcemap).
The dart sass binary can write directly to output, which could be processed with the symfony asset component to also directly make use of the versioning and such, but when writing to output you can no longer use the watch option which is kinda essential.
I believe this leaves two options:
- Run the sass compilation as it is now, but also run a process concurrently which watches for changes in the expected output files and processes them to replace paths with urls.
- Implement a watcher for the original files, run sass every time changes are detected and use the output to do the same post processing.
In both cases the suggestion by @smnandre to use sass’s relative option is useful, also the compiled css has to be parsed in both cases, for which I’m wondering if introducing a library to ‘properly’ parse it would be overkill if the alternative is regexing the sourcemap and preg_replacing its entries.
Any thoughts on these options and parsing implementation?
I'm just starting to learn about the Asset Mapper and found this issue through the documentation. I understand that a relative path does not work, but would the absolute path work?
.splash {
background-image: url('/assets/images/login-bg.png');
}
If so, perhaps instead of finding a solution for images it should be a requirement their paths are non-relative.
It's far from "not working" but there are some limitations :)
https://symfony.com/doc/current/frontend/asset_mapper.html#paths-inside-of-css-files
I noticed that base64-encoded images don't work on Sass and this bundle, although they work on CSS files and only using Asset Mapper.
It's trying to resolve the URL when it shouldn't.
For example, with this image/url:
.test{
background-image: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjEiIHZpZXdCb3g9IjAgMCAyMSAyMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSIjMDAwIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Ik0yLjU5Mi4wNDRsMTguMzY0IDE4LjM2NC0yLjU0OCAyLjU0OEwuMDQ0IDIuNTkyeiIvPjxwYXRoIGQ9Ik0wIDE4LjM2NEwxOC4zNjQgMGwyLjU0OCAyLjU0OEwyLjU0OCAyMC45MTJ6Ii8+PC9nPjwvc3ZnPg==");
}
I get an exception containing:
The file "/var/www/.../scss/%5C%22data:image/svg+xml;base64,PHN2....ZnPg==%5C%22" does not exist."
Hey @JoaoGabrielD ,
Indeed, base64-encoded images should not be processed, we probably should skip URLs starting with data:. If you want to provide a fix for this - PR is warmly appreciated!
Hello @bocharsky-bw
I've investigated a bit more, and I've noticed it only crashes with embed_sources == true
symfonycasts_sass:
# ...
sass_options:
embed_sources: true
source_map: true
The SCSS is correctly compiled into CSS, this package itself has no issues.
The actual crash happens on Symfony\AssetMapper, which is trying to compile assets from the source map's URLs in the comments.
This issue supposedly was fixed in the past, so I'll investigate a bit more before I comment on that PR.
Thanks for the research! Good news that there's no issue in the bundle itself. And thanks for looking further into it ❤️