Skip to content

Commit aedf6b6

Browse files
committed
Optimize @layer rules
fixes parcel-bundler#571
1 parent ced2046 commit aedf6b6

File tree

2 files changed

+237
-3
lines changed

2 files changed

+237
-3
lines changed

src/lib.rs

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8642,6 +8642,173 @@ mod tests {
86428642
}
86438643
"#},
86448644
);
8645+
test(
8646+
r#"
8647+
@layer a {}
8648+
@layer b {}
8649+
8650+
@layer b {
8651+
foo {
8652+
color: red;
8653+
}
8654+
}
8655+
8656+
@layer a {
8657+
bar {
8658+
color: yellow;
8659+
}
8660+
}
8661+
"#,
8662+
indoc! {r#"
8663+
@layer a {
8664+
bar {
8665+
color: #ff0;
8666+
}
8667+
}
8668+
8669+
@layer b {
8670+
foo {
8671+
color: red;
8672+
}
8673+
}
8674+
"#},
8675+
);
8676+
8677+
test(
8678+
r#"
8679+
@layer a {}
8680+
@layer b {}
8681+
8682+
@layer b {
8683+
foo {
8684+
color: red;
8685+
}
8686+
}
8687+
"#,
8688+
indoc! {r#"
8689+
@layer a;
8690+
8691+
@layer b {
8692+
foo {
8693+
color: red;
8694+
}
8695+
}
8696+
"#},
8697+
);
8698+
8699+
test(
8700+
r#"
8701+
@layer a;
8702+
@layer b;
8703+
@layer c;
8704+
"#,
8705+
indoc! {r#"
8706+
@layer a, b, c;
8707+
"#},
8708+
);
8709+
8710+
test(
8711+
r#"
8712+
@layer a {}
8713+
@layer b {}
8714+
@layer c {}
8715+
"#,
8716+
indoc! {r#"
8717+
@layer a, b, c;
8718+
"#},
8719+
);
8720+
8721+
test(
8722+
r#"
8723+
@layer a;
8724+
@layer b {
8725+
.foo {
8726+
color: red;
8727+
}
8728+
}
8729+
@layer c {}
8730+
"#,
8731+
indoc! {r#"
8732+
@layer a;
8733+
8734+
@layer b {
8735+
.foo {
8736+
color: red;
8737+
}
8738+
}
8739+
8740+
@layer c;
8741+
"#},
8742+
);
8743+
8744+
test(
8745+
r#"
8746+
@layer a, b;
8747+
@layer c {}
8748+
8749+
@layer d {
8750+
foo {
8751+
color: red;
8752+
}
8753+
}
8754+
"#,
8755+
indoc! {r#"
8756+
@layer a, b, c;
8757+
8758+
@layer d {
8759+
foo {
8760+
color: red;
8761+
}
8762+
}
8763+
"#},
8764+
);
8765+
8766+
test(
8767+
r#"
8768+
@layer a;
8769+
@layer b;
8770+
@import "a.css" layer(x);
8771+
@layer c;
8772+
8773+
@layer d {
8774+
foo {
8775+
color: red;
8776+
}
8777+
}
8778+
"#,
8779+
indoc! {r#"
8780+
@layer a, b;
8781+
@import "a.css" layer(x);
8782+
@layer c;
8783+
8784+
@layer d {
8785+
foo {
8786+
color: red;
8787+
}
8788+
}
8789+
"#},
8790+
);
8791+
8792+
test(
8793+
r#"
8794+
@layer a, b, c;
8795+
8796+
@layer a {
8797+
foo {
8798+
color: red;
8799+
}
8800+
}
8801+
"#,
8802+
indoc! {r#"
8803+
@layer a {
8804+
foo {
8805+
color: red;
8806+
}
8807+
}
8808+
8809+
@layer b, c;
8810+
"#},
8811+
);
86458812
}
86468813

86478814
#[test]

src/rules/mod.rs

Lines changed: 70 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -590,16 +590,27 @@ impl<'i, T: Clone> CssRuleList<'i, T> {
590590
if let Some(idx) = layer_rules.get(name) {
591591
if let Some(CssRule::LayerBlock(last_rule)) = rules.get_mut(*idx) {
592592
last_rule.rules.0.extend(layer.rules.0.drain(..));
593-
last_rule.minify(context, parent_is_unused)?;
594593
continue;
595594
}
596595
}
597596

598597
layer_rules.insert(name.clone(), rules.len());
599598
}
600-
if layer.minify(context, parent_is_unused)? {
601-
continue;
599+
}
600+
CssRule::LayerStatement(layer) => {
601+
// Create @layer block rules for each declared layer name,
602+
// so we can merge other blocks into it later on.
603+
for name in &layer.names {
604+
if !layer_rules.contains_key(name) {
605+
layer_rules.insert(name.clone(), rules.len());
606+
rules.push(CssRule::LayerBlock(LayerBlockRule {
607+
name: Some(name.clone()),
608+
rules: CssRuleList(vec![]),
609+
loc: layer.loc.clone(),
610+
}));
611+
}
602612
}
613+
continue;
603614
}
604615
CssRule::MozDocument(document) => document.minify(context)?,
605616
CssRule::Style(style) => {
@@ -806,6 +817,62 @@ impl<'i, T: Clone> CssRuleList<'i, T> {
806817
rules.push(rule)
807818
}
808819

820+
// Optimize @layer rules. Combine subsequent empty layer blocks into a single @layer statement
821+
// so that layers are declared in the correct order.
822+
if !layer_rules.is_empty() {
823+
let mut declared_layers = HashSet::new();
824+
let mut layer_statement = None;
825+
for index in 0..rules.len() {
826+
match &mut rules[index] {
827+
CssRule::LayerBlock(layer) => {
828+
if layer.minify(context, parent_is_unused)? {
829+
if let Some(name) = &layer.name {
830+
if declared_layers.contains(name) {
831+
// Remove empty layer that has already been declared.
832+
rules[index] = CssRule::Ignored;
833+
continue;
834+
}
835+
836+
let name = name.clone();
837+
declared_layers.insert(name.clone());
838+
839+
if let Some(layer_index) = layer_statement {
840+
if let CssRule::LayerStatement(layer) = &mut rules[layer_index] {
841+
// Add name to previous layer statement rule and remove this one.
842+
layer.names.push(name);
843+
rules[index] = CssRule::Ignored;
844+
}
845+
} else {
846+
// Create a new layer statement rule to declare the name.
847+
rules[index] = CssRule::LayerStatement(LayerStatementRule {
848+
names: vec![name],
849+
loc: layer.loc,
850+
});
851+
layer_statement = Some(index);
852+
}
853+
} else {
854+
// Remove empty anonymous layer.
855+
rules[index] = CssRule::Ignored;
856+
}
857+
} else {
858+
// Non-empty @layer block. Start a new statement.
859+
layer_statement = None;
860+
}
861+
}
862+
CssRule::Import(import) => {
863+
if let Some(layer) = &import.layer {
864+
// Start a new @layer statement so the import layer is in the right order.
865+
layer_statement = None;
866+
if let Some(name) = layer {
867+
declared_layers.insert(name.clone());
868+
}
869+
}
870+
}
871+
_ => {}
872+
}
873+
}
874+
}
875+
809876
self.0 = rules;
810877
Ok(())
811878
}

0 commit comments

Comments
 (0)