accepted/ordered-comments.md
(Issue)
This section is non-normative
When Sass introduced its new module system, the model of how stylesheets were
compiled changed as well. Instead of every source file adding CSS to one global
output stylesheet, each module produced its own CSS which was then stitched
together using more complex logic. This allowed us to support important features
like only emitting each module's CSS once, @extend scoping, and
meta.load-css(), but it did have one undesirable consequence: the order of
comments changed.
Because each module had its own separate CSS output, any loud comments that
appeared before or between that module's @use or @forward rules would end up
all together at the beginning of that module. Because that module's CSS was
always emitted in turn after its dependencies', these comments would appear to
be pushed later in the output than authors expected.
Although comment ordering doesn't affect the semantics of compiled CSS, it can be confusing and undesirable for users. This is especially true when using meaningful comments like license headers or postprocessor directives.
This section is non-normative
This proposal adjusts the order that comments are emitted relative to CSS so
that the resulting stylesheet is in essentially the same order it would have
been when using @import, without repeating stylesheets that are loaded
multiple times.
There are two potential orders that could be chosen for comments. Either would be better than the current ordering, and either can be better than the other depending on the user's needs.
The order we propose here is "traversal order", in which comments are emitted in
the same order that they're evaluated. The other plausible ordering is "linked
order", in which comments that appear directly above a @use or @forward rule
are always emitted before the module loaded by that rule.
These two orderings result in the same output when each module is loaded once, but once a given module is loaded multiple times with comments around it they become different. For example:
// _upstream.scss
a {b: c}
// styles.scss
/* before @use */
@use 'upstream';
/* before @forward */
@forward 'upstream';
produces the following outputs:
// In traversal order
/* before @use */
a {
b: c;
}
/* before @forward */
// In linked order
/* before @use */
/* before @forward */
a {
b: c;
}
Linked order makes sense when using comments to annotate information about
dependencies, but it's counterproductive when a user wants to annotate the end
of a module, since that comment would be considered linked to the next module
load. Traversal order handles that case better and matches the old @import
behavior, so we chose to use it instead.
Adjust the definition of Resolving a Module's Extensions by replacing the definition of the traversing procedure with the following:
Define a mutating recursive procedure, traversing, which takes a module
domestic:
If domestic has already been traversed, do nothing.
For each module upstream in domestic's dependencies:
For each unmarked comment in domestic's CSS, if that comment originally
appeared before the @use or @forward rule that loaded upstream, add
a copy of that comment to css and then mark it.
Traverse upstream.
Because this traverses modules depth-first, it emits CSS in reverse topological order.
Let initial-imports be the longest initial subsequence of top-level
statements in domestic's CSS tree that contains only comments and
@import rules and that ends with an @import rule.
Insert a copy of initial-imports in css after the longest initial
subsequence of comments and @import rules in css.
If there are no comments or
@importrules incss, this initial subsequence is empty andinitial-importsis inserted at the beginning ofcss.
For each top-level statement statement in domestic's CSS tree after
initial-imports:
If statement is a marked comment, ignore it.
Otherwise, add a copy of statement to the end of css, with any style
rules' selectors replaced with the corresponding selectors in
new-selectors.
@importReplace the line of the @import semantics that reads:
Add an @import with the evaluated modifiers to the current module's
CSS AST.
with one that reads:
@import with the evaluated modifiers to the current module's CSS
AST after the longest initial subsequence of comments and @import rules.This ensures that each module's CSS individually raises plain CSS
@imports to the top, and allows the procedure for combining CSS to be a bit simpler by only having to handle leading@imports.