5.4 KiB
Ordered Comments: Draft 1.0
(Issue)
Table of Contents
Background
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.
Summary
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.
Design Decisions
Traversal Order
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.
Procedures
Resolving a Module's Extensions
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
indomestic
's dependencies:-
For each unmarked comment in
domestic
's CSS, if that comment originally appeared before the@use
or@forward
rule that loadedupstream
, add a copy of that comment tocss
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 indomestic
's CSS tree that contains only comments and@import
rules and that ends with an@import
rule. -
Insert a copy of
initial-imports
incss
after the longest initial subsequence of comments and@import
rules incss
.If there are no comments or
@import
rules incss
, this initial subsequence is empty andinitial-imports
is inserted at the beginning ofcss
. -
For each top-level statement
statement
indomestic
's CSS tree afterinitial-imports
:-
If
statement
is a marked comment, ignore it. -
Otherwise, add a copy of
statement
to the end ofcss
, with any style rules' selectors replaced with the corresponding selectors innew-selectors
.
-
-
Semantics
@import
Replace 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:
- Add an
@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
@import
s to the top, and allows the procedure for combining CSS to be a bit simpler by only having to handle leading@import
s.