-
Notifications
You must be signed in to change notification settings - Fork 464
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
Fix for sourcemap offsets #2208
Conversation
Thank you for this. I wont have time to look it over until next week. In the mean times it's worth noting we purposely differ from Ruby Sass in some cases to provide a better experience in non-spec compliant browsers. |
Thank you for the update. Feedback is appreciated. |
First thanks for your contributions. For some background info on sourcemaps. They were first implement in #207. I have then further improved it begining with #591. Since then no real cleanup has been done, as the initial applied architecture proved to have some unexpected issues, therefore the state of sourcemaps have always been kind of unknown. I guess you mostly relate your expectations regarding sourcemap according to how gemini-coverage behaves? IMO sourcemaps V3 is still just a draft/proposal and I'll try to explain why libsass creates the sourcemaps as it currently does. We had lengthy discussions (//cc @am11) if we want to create just a very basic sourcemap (as ruby sass does) or a more elaborated one (as less does). We decided to try the second. I also already want to point out that sourcemap has no concept of opening and closing mappings. AFAIR there is no concept of "enclosing" ranges for sourcemaps. Libsass does not output evenly distributed pairs of start-end mappings (explanations why this is follows). .foo { // maps to line 1
background: red;
&-bar { // also maps to line 1
background: green;
}
} SourceMap Inspector (click [inspect] and use keyboard arrows to navigate) Going to try to explain the mappings libsass produces (see it in the inspector linked above): As @xzyfer already pointed out, we added one crutch to libsass (you can read up that in the linked issue) which causes a duplicate starting mapping (AAAA). Afterwards we have a start-end mapping for the I agree that we could argue if the mapping for the parent selector should point to where the IMHO this is a perfectly valid mapping according to the specs. We tried to do our best to get the best support for browsers. And different tools may take different assumptions. So why does libsass not output even sizes start-end position mappings? One reason is ie. how we could map variables. In theory we could add mappings for every variable, number etc. that was involved in creating the final output I just re-scanned the proposal and it seem that at least the following was added since a last read it (not sure if it addresses the issues we face here, namely that a lot is still not specified):
Another example why getting it right is difficult: Take the selector Unfortunately your other examples can also be explained and do IMO not violate against the current SourceMap draft. IMO you can only call the behavior of libsass buggy under certain assumptions which are IMO not given by the current spec. Not sure if one interpretation of the current proposal by one tool is enough to change the current behavior by libsass (since the mappings are IMO technically correct, beside the mentioned crutch we had to add for browsers, and beside the few obvious missmaps, but they are mostly just in col and not in line). $color: green;
.foo {
background: $color; // starts in line 3, ends in line 1
} SourceMap Inspector (click [inspect] and use keyboard arrows to navigate) This one might actually contain one wrong mapping .foo {
background: red;
.bar {
background: green;
}
} SourceMap Inspector (click [inspect] and use keyboard arrows to navigate) This one has a wrong col offset mapping for "screen". .foo {
background: red;
@media only screen {
background: green;
}
} SourceMap Inspector (click [inspect] and use keyboard arrows to navigate) In regard to Sorry to not have any better news. I really really appreciate your effort. I know that going through libsass source code is not an easy thing and we are in need of good c++ coders here, so I would have hoped for a better outcome. Of course feel free to rebuttal, but I hope we can agree that the main ground should be the SourceMap V3 Proposal. We do tend to follow what browsers do if necessary, as they are really the 1st customers of sourcemaps. So unfortunately I have to decline this PR. Maybe @xzyfer or @am11 see it different. IMO to consider this, the tool in question would need to have a very broad user base, which gemini-coverage doesn't seem to have yet. This pretty much sums up all the information I have about the state of libsass sourcemaps. With my very best regards |
Thank you for the very detailed explanation. I'm quite aware of the fact that my PR is kind of a hotfix to make it work within Chrome and Gemini. I wished I could fix it in a better way, but as you also noticed, the current design of libsass doesn't allow for that.
Can you link to the section which states something about the way sourcemaps should look like? To me, this proposal only defines the format of the sourcemap file, so how the final JSON should look, not where sourcemap offsets should look like.
This is perfectly fine and I understand the concept. Maybe I did not make it clear enough in my explanation. However, the name of the functions
I saw that crutch in the source, read the PR, and in my opinion it was at the wrong position. I put it into the rendering of the
Well, the main problem why I started with all this was the fact that every selector that is blended with the parent selector
In my opinion, the specs state nothing about how mappings should look like, so every mapping is perfectly valid, as long as the final JSON can be parsed ;-)
Sourcemaps are broken in Chrome (see the linked issue), so the crutch that was added doesn't solve all the problems.
Again, in my opinion, the spec doesn't say anything on mappings, so every mapping is valid. However, I do understand the point that you don't want to change things the way I proposed because it may break stuff downstream, so I'm fine with that.
This is wrong, because Would you be okay with merging the changes only made in this commit (I can revert everything else in my PR)? This would already fix a lot for us. |
I have create #2216 which should address your issues.
These make sense in terms of how we parse stuff, as there we always have a start and end position, that's why these methods were named that way. Since then the code has evolved and doesn't fit well together in all ends. This works well for tokens that are parsed with one lex call. But doesn't work so well for containers. That's why I said that the whole thing would need a refactoring so everything makes sense together again. Nonetheless it is quite workable and stable, if one knows where to look exactly (debug_ast ist a big help as it shows you the mappings for every node).
I disagree, since I think with #2216 all the reported issues should be solved. There are probably quite a few other places that would need similar fixing. I guess #2216 should show a few ways how to fix such sourcemap issues. Most should have similar causes as either not having the right mapping after parsing or that we don't update the mappings after operations. |
Merged #2216 |
This PR fixes many issues with incorrect source mappings as reported in #2204. Unfortunately with the current design of libsass it's not possible to fix all issues (read on).
Issues fixed
Rules with parent selectors result in incorrect source mappings
Sequence_Selector::resolve_parent_refs
replaces&
with the actual name of the parent selector. The problem is that parent selectors are cloned with their original positions so any rule that directly extends a parent selector gets the original position of the parent selector, resulting in duplicate mappings. This is also why Google Chrome shows the same line numbers for the following rules:Result:
Emitted source mappings for compound classes differ from ruby-sass
Nested definitions that result in compound class definitions when compiled result in source mappings for each of the classes:
This results in separate source mappings for
.foo
and.bar
. ruby-sass emits one source mapping for.foo .bar
which is also more correct and more compatible with tools that rely on those mappings. This was fixed by fleshing outemitter.cpp
and scheduling the open mapping inInspect::operator()(Sequence_Selector* c)
, and the close mapping inInspect::operator()(CommaSequence_Selector* g)
.Result:
Furthermore, most of the calls to
append_token
which add source mappings on their own without respecting the context have been replaced byappend_string
and specific source mappings in the callers. Source mappings were also generated for keywords or rules that don't make it into the final CSS, e.g.@return
,@warn
,@debug
. Those have also been removed, because there is no direct representation in the compiled source.Declarations with variables result in incorrect line numbers
When a variable is referenced in a declaration, the name of the variable and trailing semicolon map to the variable definition, but not to the position where it is referenced. This doesn't break Chrome source mappings (they are shown correctly when the classes are mapped correctly), but Gemini CSS Coverage or other tools that rely on source mappings for declarations.
This was fixed by omitting source mappings for all parts of the declaration, eg.
background
,:
,green
,;
and consolidating it into one mappingbackground: green;
. The offset of the mapping in the original source is not absolutely correct, because the length of the value is not included in the parser state, so the mapping ends at the end ofbackground
.Result:
Issues that won't fix
While the noise in sourcemaps could be largely reduced and they appear to be actually usable, there are some cases that are, at least in my opinion, not fixable by the current design of libsass.
Inline blocks for
@media
and@supports
Media queries that do not scope the contained declarations into selectors, but are contained within a selector (inline) do not map correctly. The selector is inserted by the parser (it's an implied
&
), but since there is no direct representation of the implied parent in the original source, the parent refers to the original parent, which results in a double mapping that breaks offsets again.Result:
.foo
However, in the proposed PR, this can be omitted by explicitly using the parent selector:
Result:
Sourcemap offsets are largely incorrect
The fact that libsass implements a multi-step approach (parse, check nesting, expand, check nesting, render) and that during transformation/expansion selectors and expressions are irreversibly overwritten, original offsets are irreversibly lost. Digging into the code and architecture of libsass (which in large parts is very well written!), I come to the conclusion that the emission of correct source mappings – correct lines and columns, the latter of which are currently largely incorrect – involves a huge rewrite. Some concrete examples:
This PR fixes all issues that we currently have with Gemini CSS coverage (using the
@media
workaround). I'm very excited to hear your ideas on this PR and hope it finds its way into master, since it should be of good use for all of us.