SymfonyCasts / sass-bundle

Delightful Sass Support for Symfony + AssetMapper
https://symfony.com/bundles/SassBundle/current/index.html
MIT License
35 stars 18 forks source link

Relative url() paths in compiled Sass files can be wrong #2

Open weaverryan opened 1 year ago

weaverryan commented 1 year ago

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:

1) 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.

2) When the final .css file 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 .css file 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

smnandre commented 10 months ago

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

dorxy commented 10 months ago

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:

  1. 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.
  2. 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?

justin-oh commented 6 months ago

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.

smnandre commented 6 months ago

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