-
Notifications
You must be signed in to change notification settings - Fork 3.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
data-uri function uses path of data-uri call, not the string with the path to the file in. #2629
Comments
FYI: You can elegantly implement this without breaking backwards-compatibility in a very simple way. Currently the E.g.
|
For me it looks more like a workaround rather than a fix. At further iteration they will try to avoid verbosity by moving // mixins.less
.background(@image) {
background-image: data-uri("@{image}.jpg");
}
// app/content/button.less
button {
.background("images/btn");
} (E.g. for a font-face mixin as an example it's usually multiple |
Then I don't really think there's a suitable solution period. You need some way of determining the context against which a relative URL should resolve. Either you take that context from the file info of the file that defined the string value going into the If you want to support path variable substitution, then things quickly become tricky because you need to figure out how the various parts of the path need to be normalized. E.g. , how do you normalize something like: // mixins.less
.background(@image) {
background-image: data-uri("../../@{image}.jpg");
}
// app/content/button.less
button {
.background("../images/btn");
} How would you combine the relative url resolution and the token substitution-driven combination of those two paths? I suppose one option is to only resolve against the file defining the string that is being substituted for a replacer token, when the replacer token is at the head of the final URL value going into a Another solution could be to introduce some path handling functions to join paths or add/remove/edit file extensions (similar to how the E.g. // mixins.less
.background(@image) {
background-image: data-uri(extension(@image, "jpg"));
}
// app/content/button.less
button {
.background(url("./images/btn"));
} |
For your first example, it does not need any special normalization, After all if it will be supposed to work "as expected" only with In other words what I mean is that if this is to be fixed then it should be fixed strait with More over, since `url` actually suffers from the same issue itself with `--relative-urls: on`, technically I'd expect the same to be changed same way for `url` then too (unfortunately this will be even more breaking change). In other words, assuming `url` and `data-uri` were initially designed as interchangeable (i.e. `data-uri` is just a special version of `url` (and compiles to CSS `url` in the end)), it would be quite painful to describe why exactly `ulr` behaves "like this" (with options like that) while `data-uri` behaves "like that" (with options like this), and to get certain behavior you'll need `data-uri(url())` combo, limited to "`data-uri` inside a mixin and `url` _out_ of it with `--relative-urls: on`" <- Bhrrrr... :) |
Yeah I agree. I'm slowly trying to build up to putting some more effort into less for a v3 release and just fixing this. I was thinking of working out all the possible file paths and trying them one by one - though thats a bit nasty if there are multiple files at different locations... I think I agree its not possible to completely fix this forever, but at least we can fix the normal case. Possibly a Sorry just quickly jotting down thoughts. |
This is just a quicknote, but we will have very same problem with import using variable interpolation. |
That is both Interesting and unsettling. |
I'm not sure I want to support variable |
A lot of discussion about workaround, maybe just fix the actual problem? Isn't it clear that data-uri should be relative to file being processed. Right now I have file which imports a reference from another path, and it still complains about data-uri. At least that must be a bug? I mean if I import by reference it should not try to rewrite the paths to relative to current file. |
Based on what exactly? What does it have to do with
It sounds like the opposite to the issue above. Could you provide more details? (e.g. paths of the importing, imported and data files etc.). |
styles.less
sub/test.less
It compiles style.less, but not the test.less because it tries to use data-uri relative to test.less. Why would reference import try to do rewriting the paths, it's not necessary when using references in my opinion. |
I would expect data-uri to follow the url rewriting rules in the options. Of course.... hard to say what that actually means with data-uri. In general, data-uri should resolve relative to the "calling .less file". So in the case of a mixin, it should resolve relative to where the mixin is called, not relative to the mixin location. The mixin "mixes in" those statements and then resolves them. So @lukeapage I think your interpretation is correct:
It should look for btn.jpg at |
That said... I don't think there would be an issue with resolving like:
Node.js attempts multiple paths for resolution. As long as the resolution order is clearly documented, you can manage expectations. And doing it like that would mean that if someone had based their .less to do behavior #2, it would still work in almost all, if not all cases. |
You couldn't ever make that work in a way that is generally correct for all use-cases. E.g. how would you support parametrized urls, i.e., urls with replacement tokens, that should be resolved against some known base folder with just the replacement tokens filled? That case requires resovling against the callee's file, not the caller's file. I had a bit of an epiphany on how to fix this transparently without explicit use of When literal That keeps everything working as expected, save for scenario's with substitution strings such as in: // mixins.less
.background(@image) {
background-image: data-uri("@{image}.jpg");
}
// app/content/button.less
button {
.background("images/btn");
} There's a trick you can pull to fix those as well: when filling out the subsitution tokens, if a token is at the very start of the substitution string, then the resulting string should inherit the file info from the filled in token, rather than that of the subsitution string. If the intent of the mixin's own author is to have such paths resolve against the mixin file, they can still make that work by using, e.g., |
// _mixins.less
.sprite(@image) {
background: data-uri("../images/sprites/@{image}.png") no-repeat;
} // main.less
div {
.sprite('logo');
} output: div {
background: url(data:image/png;base64,...) no-repeat;
} |
Wow, this is an very old one.... my two cents as this issue is affecting me as well. What about adding the option to url function, or creating a new function, that will solve the url as absolute. That way no matter where the mixin is set, it will be working with absolute paths and no room for mistakes. |
The issue here was not about absolute vs relative paths. It was about relative against call site vs relative against declaration site. Totally different problem. |
Perhaps the issue has evolved, but the initial comment and title is about data-uri resolving the path in relation to the file where it was being called, which when combined with a mixin placed somewhere else may resolve the path incorrectly. Well, that's the issue I am experiencing. |
So where do absolute paths factor into that as a solution? |
Assuming data-uri accepts absolute paths and there is an hipotethical // mixins.less
.background(@image) {
background-image: data-uri(@image);
} // app/content/button.less
button {
.background(absolute-url("images/btn.jpg"));
} |
@miljan-aleksic By "absolute" do you mean relative to the file at the location of It seems like a functional wrapper for a url to make any url file-relative is a good approach though. Or an additional argument to url(). |
@matthew-dean, by absolute I mean full path to the file, eg: I don't get your approach as I don't see how url() could make a relative path to data-uri location file without knowing it ubication. |
Funnily enough; that's almost what I suggested a few years ago. ;-)
It looks like with absolute you mean this I.e. you both mean the same thing. As did I, at the time. |
As in, rewrite URLs or rootpath would still apply, but the based on the definition location? That seems a little different than @lukeapage's original example, which was not talking about URLs relative to output, but URLs during compile; e.g. the So this issue is a little hard to track because people have posted about similar, but not exactly identical issues. That is, changing a relative source would probably require a much different solution than changing relative output. Or perhaps not; it depends on the pathing logic; but we should be clear that data-uri doesn't produce any path as output. Maybe we need something like a The tricky part about all of this is all the rewrite URL options, of which there are now more as of the PR merge that added module support. (#3248). So if it returns a file-relative URL, can it still be rewritten? I would assume yes, but we'd need to be clear. |
Yes, resolve() and file() defines exactly what I was trying to explain. I hope we could see this implemented in some near future. |
@miljan-aleksic Okay, that makes sense then. Actually, What about:
So, two things added: 1) a resolve() function to combine paths, 2) a |
^Bingo. Exactly that.
I'd go for the Node.js-y option: |
Err... that wouldn't match Less / CSS keyword nor Less variable nor function semantics. We'd have to do better than that. |
A double leading underscore is used to indicate 'something the system provides' in a lot of languages, especially when it comes to things like intrinsic variables. So the out-of-placeness is kind of the point there. Ofcourse; if you don't like it, you could always go with a derivative like However, having thought about this some more, why do we even need the current file path exposed as a variable? Can't it be woven into the conceptual |
And we got back to my proposal, just with a different function name. I still like it the most, even better as resolve(). |
Sort of. What I'm getting at is that afaik there isn't a need to pass in the URL / path of the file holding the call to a
Line 47 in 4e903e8
If I'm reading the code correctly, the file info there corresponds to the file info of the place where the function call is declared - not the file where the function call is evaluated. That means it should be OK to use this file info for an 'eager' URL resolver. It would act as expected, even if a function call is placed inside a mixin that is imported and evaluated within the context of another file. Namely: with the resolution pinned to the file where the mixin is defined. |
If we're sure no one needs the current file or a general resolver function... maybe... the thing is an explicit local var makes it clear that it's not a generic function, and that the function won't be evaluated like any other function. That's my real concern, is the semantics. No matter what you name it, if you don't have a special "marker" for "current file", then it's not like any other function that's resolved the same based on inputs. In other words, it's a function that resolves according to invisible inputs, and that concerns me. However, if the function is something extremely explicit like So, no, a local var is technically not needed, but the meaning / output / behavior needs to be clear from the semantics. |
hmm... Ok. Then how about we have a I.e. a full form call (without implicit base) would be something like:
... and would be equivalent to just doing
... which is the eager equivalent to the lazy
No 'automagically' injected variables needed that way. This could be implemented purely as a set of plugin functions, I think. And the only core mechanic we need is a way to mark URLs as 'already resolved', so that the default resolver logic can be blocked from running on URLs that come out of the The semantics should be made clear in documentation ofcourse. And that documentation can explicitly compare |
Just in case the "injected variables" idea won't work (w/o additional hacks) anyway because imported files have the same scope. I.e.: @__dir: "whatever";
// *everywhere* it's the only @__dir value = the path of "c"
@import "a";
@import "b";
@import "c"; Speaking of the function-based implementation, I think (but can't be sure) that it's still possible to obtain the path of the file where the function is invoked somewhere in its |
emmmm, so we don't have a better way to resolve it? |
Lazy evaluation makes this very hard to solve properly, I'm afraid.
You should be able to find the |
from #2541 but I've seen this in projects
I would expect the image to be fetched from
app/content/images/btn.jpg
but it is fetched fromimages/btn.jpg
.I welcome feedback on whether this is a breaking change (warranting a major up) or a bug fix.
The text was updated successfully, but these errors were encountered: