fix(css/prefixer): Handle at-rules and don't generate unnecessary prefixes (#4318)

This commit is contained in:
Alexander Akait 2022-04-13 11:28:09 +03:00 committed by GitHub
parent 8959774a78
commit ac4f14ad7b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 360 additions and 80 deletions

View File

@ -437,11 +437,10 @@ macro_rules! str_to_ident {
#[derive(Default)] #[derive(Default)]
struct Prefixer { struct Prefixer {
in_stylesheet: bool,
in_media_query_list: bool,
in_keyframe_block: bool, in_keyframe_block: bool,
simple_block: Option<SimpleBlock>,
added_rules: Vec<Rule>, added_rules: Vec<Rule>,
rule_prefix: Option<Prefix>,
simple_block: Option<SimpleBlock>,
added_declarations: Vec<Declaration>, added_declarations: Vec<Declaration>,
} }
@ -498,6 +497,7 @@ impl Prefixer {
} }
} }
#[derive(Clone, Debug, PartialEq)]
pub enum Prefix { pub enum Prefix {
Webkit, Webkit,
Moz, Moz,
@ -506,15 +506,130 @@ pub enum Prefix {
} }
impl VisitMut for Prefixer { impl VisitMut for Prefixer {
fn visit_mut_stylesheet(&mut self, n: &mut Stylesheet) {
let mut new = vec![];
for mut n in take(&mut n.rules) {
n.visit_mut_children_with(self);
new.append(&mut self.added_rules);
new.push(n);
}
n.rules = new;
}
// TODO handle declarations in `@media`/`@support` // TODO handle declarations in `@media`/`@support`
// TODO handle `@viewport` // TODO don't generate wrong prefixes in prefixed selectors and at-rules
// TODO handle `@keyframes` fn visit_mut_at_rule(&mut self, n: &mut AtRule) {
let mut added_at_rules = vec![];
// TODO avoid duplicate
match &n.name {
AtRuleName::Ident(Ident { value, .. })
if value.as_ref().eq_ignore_ascii_case("viewport") =>
{
let new_name = AtRuleName::Ident(Ident {
span: DUMMY_SP,
value: "-ms-viewport".into(),
raw: "-ms-viewport".into(),
});
added_at_rules.push((
Some(Prefix::Ms),
AtRule {
span: DUMMY_SP,
name: new_name,
prelude: n.prelude.clone(),
block: n.block.clone(),
},
));
let new_name = AtRuleName::Ident(Ident {
span: DUMMY_SP,
value: "-o-viewport".into(),
raw: "-o-viewport".into(),
});
added_at_rules.push((
Some(Prefix::O),
AtRule {
span: DUMMY_SP,
name: new_name,
prelude: n.prelude.clone(),
block: n.block.clone(),
},
));
}
AtRuleName::Ident(Ident { value, .. })
if value.as_ref().eq_ignore_ascii_case("keyframes") =>
{
let new_name = AtRuleName::Ident(Ident {
span: DUMMY_SP,
value: "-webkit-keyframes".into(),
raw: "-webkit-keyframes".into(),
});
added_at_rules.push((
Some(Prefix::Webkit),
AtRule {
span: DUMMY_SP,
name: new_name,
prelude: n.prelude.clone(),
block: n.block.clone(),
},
));
let new_name = AtRuleName::Ident(Ident {
span: DUMMY_SP,
value: "-moz-keyframes".into(),
raw: "-moz-keyframes".into(),
});
added_at_rules.push((
Some(Prefix::Moz),
AtRule {
span: DUMMY_SP,
name: new_name,
prelude: n.prelude.clone(),
block: n.block.clone(),
},
));
let new_name = AtRuleName::Ident(Ident {
span: DUMMY_SP,
value: "-o-keyframes".into(),
raw: "-o-keyframes".into(),
});
added_at_rules.push((
Some(Prefix::O),
AtRule {
span: DUMMY_SP,
name: new_name,
prelude: n.prelude.clone(),
block: n.block.clone(),
},
));
}
_ => {}
}
n.visit_mut_children_with(self);
for mut rule in take(&mut added_at_rules) {
let old_rule_prefix = self.rule_prefix.clone();
self.rule_prefix = rule.0;
rule.1.visit_mut_children_with(self);
self.added_rules.push(Rule::AtRule(rule.1));
self.rule_prefix = old_rule_prefix;
}
}
fn visit_mut_media_query_list(&mut self, media_query_list: &mut MediaQueryList) { fn visit_mut_media_query_list(&mut self, media_query_list: &mut MediaQueryList) {
let old_in_media_query_list = self.in_media_query_list;
self.in_media_query_list = true;
let mut new = vec![]; let mut new = vec![];
for mut n in take(&mut media_query_list.queries) { for mut n in take(&mut media_query_list.queries) {
@ -560,8 +675,6 @@ impl VisitMut for Prefixer {
} }
media_query_list.queries = new; media_query_list.queries = new;
self.in_media_query_list = old_in_media_query_list;
} }
fn visit_mut_keyframe_block(&mut self, n: &mut KeyframeBlock) { fn visit_mut_keyframe_block(&mut self, n: &mut KeyframeBlock) {
@ -574,14 +687,9 @@ impl VisitMut for Prefixer {
self.in_keyframe_block = old_in_keyframe_block; self.in_keyframe_block = old_in_keyframe_block;
} }
// TODO don't generate wrong prefixes in prefixed selectors
fn visit_mut_qualified_rule(&mut self, n: &mut QualifiedRule) { fn visit_mut_qualified_rule(&mut self, n: &mut QualifiedRule) {
n.visit_mut_children_with(self); n.visit_mut_children_with(self);
if !self.in_stylesheet {
return;
}
if let QualifiedRulePrelude::Invalid(_) = n.prelude { if let QualifiedRulePrelude::Invalid(_) = n.prelude {
return; return;
} }
@ -700,23 +808,28 @@ impl VisitMut for Prefixer {
} }
} }
fn visit_mut_stylesheet(&mut self, n: &mut Stylesheet) { fn visit_mut_simple_block(&mut self, simple_block: &mut SimpleBlock) {
let old_in_stylesheet = self.in_stylesheet; let old_simple_block = self.simple_block.clone();
self.in_stylesheet = true; self.simple_block = Some(simple_block.clone());
let mut new = vec![]; let mut new = vec![];
for mut n in take(&mut n.rules) { for mut n in take(&mut simple_block.value) {
n.visit_mut_children_with(self); n.visit_mut_children_with(self);
new.append(&mut self.added_rules); new.extend(
self.added_declarations
.drain(..)
.map(DeclarationOrAtRule::Declaration)
.map(ComponentValue::DeclarationOrAtRule),
);
new.push(n); new.push(n);
} }
n.rules = new; simple_block.value = new;
self.in_stylesheet = old_in_stylesheet; self.simple_block = old_simple_block;
} }
fn visit_mut_declaration(&mut self, n: &mut Declaration) { fn visit_mut_declaration(&mut self, n: &mut Declaration) {
@ -839,52 +952,82 @@ impl VisitMut for Prefixer {
// TODO avoid duplication insert // TODO avoid duplication insert
macro_rules! add_declaration { macro_rules! add_declaration {
($prefix:expr,$name:expr) => {{ ($prefix:expr,$name:expr) => {{
let need_prefix = match self.get_declaration_by_name($name) { // Use only specific prefix in prefixed at-rules or rule, i.e.
Some(_) => false, // don't use `-moz` prefix for properties in `@-webkit-keyframes` at-rule
_ => true, let need_prefix = if let Some(rule_prefix) = &self.rule_prefix {
if $prefix != *rule_prefix {
false
} else {
true
}
} else {
true
}; };
if need_prefix { if need_prefix {
let name = DeclarationName::Ident(Ident { // Check we don't have prefixed property
span: DUMMY_SP, let need_prefix = match self.get_declaration_by_name($name) {
value: $name.into(), Some(_) => false,
raw: $name.into(), _ => true,
});
let new_value = match $prefix {
Prefix::Webkit => webkit_value.clone(),
Prefix::Moz => moz_value.clone(),
Prefix::O => o_value.clone(),
Prefix::Ms => ms_value.clone(),
}; };
self.added_declarations.push(Declaration { if need_prefix {
span: n.span, let name = DeclarationName::Ident(Ident {
name, span: DUMMY_SP,
value: new_value, value: $name.into(),
important: n.important.clone(), raw: $name.into(),
}); });
let new_value = match $prefix {
Prefix::Webkit => webkit_value.clone(),
Prefix::Moz => moz_value.clone(),
Prefix::O => o_value.clone(),
Prefix::Ms => ms_value.clone(),
};
self.added_declarations.push(Declaration {
span: n.span,
name,
value: new_value,
important: n.important.clone(),
});
}
} }
}}; }};
($prefix:expr,$name:expr,$value:expr) => {{ ($prefix:expr,$name:expr,$value:expr) => {{
let need_prefix = match self.get_declaration_by_name($name) { // Use only specific prefix in prefixed at-rules or rule, i.e.
Some(_) => false, // don't use `-moz` prefix for properties in `@-webkit-keyframes` at-rule
_ => true, let need_prefix = if let Some(rule_prefix) = &self.rule_prefix {
if $prefix != *rule_prefix {
false
} else {
true
}
} else {
true
}; };
if need_prefix { if need_prefix {
let name = DeclarationName::Ident(Ident { // Check we don't have prefixed property
span: DUMMY_SP, let need_prefix = match self.get_declaration_by_name($name) {
value: $name.into(), Some(_) => false,
raw: $name.into(), _ => true,
}); };
self.added_declarations.push(Declaration { if need_prefix {
span: n.span, let name = DeclarationName::Ident(Ident {
name, span: DUMMY_SP,
value: $value, value: $name.into(),
important: n.important.clone(), raw: $name.into(),
}); });
self.added_declarations.push(Declaration {
span: n.span,
name,
value: $value,
important: n.important.clone(),
});
}
} }
}}; }};
} }
@ -2249,28 +2392,4 @@ impl VisitMut for Prefixer {
}); });
} }
} }
fn visit_mut_simple_block(&mut self, simple_block: &mut SimpleBlock) {
let old_simple_block = self.simple_block.clone();
self.simple_block = Some(simple_block.clone());
let mut new = vec![];
for mut n in take(&mut simple_block.value) {
n.visit_mut_children_with(self);
new.extend(
self.added_declarations
.drain(..)
.map(DeclarationOrAtRule::Declaration)
.map(ComponentValue::DeclarationOrAtRule),
);
new.push(n);
}
simple_block.value = new;
self.simple_block = old_simple_block;
}
} }

View File

@ -1,3 +1,75 @@
@-webkit-keyframes anim {
from {
top: -webkit-calc(10% + 10px);
top: -moz-calc(10% + 10px);
top: calc(10% + 10px);
-webkit-transform: rotate(10deg);
transform: rotate(10deg);
}
50% {
top: 0;
display: -webkit-box;
display: -webkit-flex;
display: -moz-box;
display: -ms-flexbox;
display: flex;
}
to {
top: -webkit-calc(10%);
top: -moz-calc(10%);
top: calc(10%);
-webkit-transform: rotate(0);
transform: rotate(0);
}
}
@-moz-keyframes anim {
from {
top: -webkit-calc(10% + 10px);
top: -moz-calc(10% + 10px);
top: calc(10% + 10px);
-moz-transform: rotate(10deg);
transform: rotate(10deg);
}
50% {
top: 0;
display: -webkit-box;
display: -webkit-flex;
display: -moz-box;
display: -ms-flexbox;
display: flex;
}
to {
top: -webkit-calc(10%);
top: -moz-calc(10%);
top: calc(10%);
-moz-transform: rotate(0);
transform: rotate(0);
}
}
@-o-keyframes anim {
from {
top: -webkit-calc(10% + 10px);
top: -moz-calc(10% + 10px);
top: calc(10% + 10px);
-o-transform: rotate(10deg);
transform: rotate(10deg);
}
50% {
top: 0;
display: -webkit-box;
display: -webkit-flex;
display: -moz-box;
display: -ms-flexbox;
display: flex;
}
to {
top: -webkit-calc(10%);
top: -moz-calc(10%);
top: calc(10%);
-o-transform: rotate(0);
transform: rotate(0);
}
}
@keyframes anim { @keyframes anim {
from { from {
top: -webkit-calc(10% + 10px); top: -webkit-calc(10% + 10px);
@ -26,9 +98,36 @@
transform: rotate(0); transform: rotate(0);
} }
} }
@-webkit-keyframes inside {}
@-moz-keyframes inside {}
@-o-keyframes inside {}
@media screen { @media screen {
@keyframes inside {} @keyframes inside {}
} }
@-webkit-keyframes spaces {
from {
color: black;
}
to {
color: white;
}
}
@-moz-keyframes spaces {
from {
color: black;
}
to {
color: white;
}
}
@-o-keyframes spaces {
from {
color: black;
}
to {
color: white;
}
}
@keyframes spaces { @keyframes spaces {
from { from {
color: black; color: black;

View File

@ -34,6 +34,24 @@ em {
-moz-transform: rotateZ(45deg); -moz-transform: rotateZ(45deg);
transform: rotateZ(45deg); transform: rotateZ(45deg);
} }
@-webkit-keyframes anim {
from {
-webkit-transform: rotate(90deg);
transform: rotate(90deg);
}
}
@-moz-keyframes anim {
from {
-moz-transform: rotate(90deg);
transform: rotate(90deg);
}
}
@-o-keyframes anim {
from {
-o-transform: rotate(90deg);
transform: rotate(90deg);
}
}
@keyframes anim { @keyframes anim {
from { from {
-webkit-transform: rotate(90deg); -webkit-transform: rotate(90deg);

View File

@ -0,0 +1,4 @@
@viewport {
min-width: 640px;
max-width: 800px;
}

View File

@ -0,0 +1,12 @@
@-ms-viewport{
min-width: 640px;
max-width: 800px;
}
@-o-viewport{
min-width: 640px;
max-width: 800px;
}
@viewport{
min-width: 640px;
max-width: 800px;
}

View File

@ -66,12 +66,40 @@
-webkit-writing-mode: sideways-lr; -webkit-writing-mode: sideways-lr;
writing-mode: sideways-lr; writing-mode: sideways-lr;
} }
@-ms-viewport{
-ms-writing-mode: rl-tb;
writing-mode: horizontal-tb;
direction: rtl;
}
@-o-viewport{
writing-mode: horizontal-tb;
direction: rtl;
}
@viewport{ @viewport{
-webkit-writing-mode: horizontal-tb; -webkit-writing-mode: horizontal-tb;
-ms-writing-mode: rl-tb; -ms-writing-mode: rl-tb;
writing-mode: horizontal-tb; writing-mode: horizontal-tb;
direction: rtl; direction: rtl;
} }
@-webkit-keyframes test {
100% {
-webkit-writing-mode: horizontal-tb;
writing-mode: horizontal-tb;
direction: rtl;
}
}
@-moz-keyframes test {
100% {
writing-mode: horizontal-tb;
direction: rtl;
}
}
@-o-keyframes test {
100% {
writing-mode: horizontal-tb;
direction: rtl;
}
}
@keyframes test { @keyframes test {
100% { 100% {
-webkit-writing-mode: horizontal-tb; -webkit-writing-mode: horizontal-tb;