csskit_transform/
remove_inert_nodes.rs1use crate::prelude::*;
2use css_ast::{VisitNode, Visitable};
3
4pub struct RemoveInertNodes<'a, 'ctx, N: Visitable + NodeWithMetadata<CssMetadata>> {
7 pub transformer: &'ctx Transformer<'a, CssMetadata, N, CssMinifierFeature>,
8}
9
10impl<'a, 'ctx, N> Transform<'a, 'ctx, CssMetadata, N, CssMinifierFeature> for RemoveInertNodes<'a, 'ctx, N>
11where
12 N: Visitable + NodeWithMetadata<CssMetadata>,
13{
14 fn skips_subtree(metadata: &CssMetadata) -> bool {
15 !metadata.is_inert()
16 }
17
18 fn new(transformer: &'ctx Transformer<'a, CssMetadata, N, CssMinifierFeature>) -> Self {
19 Self { transformer }
20 }
21}
22
23#[visitor]
24impl<'a, 'ctx, N> Visit for RemoveInertNodes<'a, 'ctx, N>
25where
26 N: Visitable + NodeWithMetadata<CssMetadata>,
27{
28 fn consider_node(&self, node: VisitNode) -> VisitFlow {
29 if node.node_id.is_some() && Self::skips_subtree(&node.subtree_metadata()) {
30 return VisitFlow::SKIP_CHILDREN;
31 }
32 VisitFlow::DESCEND
33 }
34
35 fn enter_node(&mut self, node: VisitNode) -> VisitFlow {
36 if node.self_metadata().is_inert() {
37 self.transformer.delete(node.span);
38 return VisitFlow::SKIP_CHILDREN;
39 }
40 VisitFlow::DESCEND
41 }
42}
43
44#[cfg(test)]
45mod tests {
46 use crate::test_helpers::{assert_no_transform, assert_transform};
47 use css_ast::{CssAtomSet, StyleSheet};
48
49 #[test]
50 fn removes_empty_style_rule() {
51 assert_transform!(
52 CssMinifierFeature::RemoveInertNodes,
53 CssAtomSet,
54 StyleSheet,
55 "a {}\nb { color: red }",
56 "b { color: red }"
57 );
58 }
59
60 #[test]
61 fn removes_empty_media_rule() {
62 assert_transform!(
63 CssMinifierFeature::RemoveInertNodes,
64 CssAtomSet,
65 StyleSheet,
66 "@media screen {}\nb { color: red }",
67 "b { color: red }"
68 );
69 }
70
71 #[test]
72 fn removes_empty_nested_rule() {
73 assert_transform!(
74 CssMinifierFeature::RemoveInertNodes,
75 CssAtomSet,
76 StyleSheet,
77 "a { color: red; & b {} }",
78 "a { color: red; }"
79 );
80 }
81
82 #[test]
83 fn removes_rule_containing_only_inert_rules() {
84 assert_transform!(
85 CssMinifierFeature::RemoveInertNodes,
86 CssAtomSet,
87 StyleSheet,
88 "nav { a {} }\nb { color: red }",
89 "b { color: red }"
90 );
91 }
92
93 #[test]
94 fn removes_keyframes_with_only_empty_keyframes() {
95 assert_transform!(
96 CssMinifierFeature::RemoveInertNodes,
97 CssAtomSet,
98 StyleSheet,
99 "@keyframes fade { 0% {} 100% {} }\nb { color: red }",
100 "b { color: red }"
101 );
102 }
103
104 #[test]
105 fn removes_empty_font_face_rule() {
106 assert_transform!(
107 CssMinifierFeature::RemoveInertNodes,
108 CssAtomSet,
109 StyleSheet,
110 "@font-face {}\nb { color: red }",
111 "b { color: red }"
112 );
113 }
114
115 #[test]
116 fn keeps_rule_with_declarations() {
117 assert_no_transform!(CssMinifierFeature::RemoveInertNodes, CssAtomSet, StyleSheet, "a { color: red }");
118 }
119
120 #[test]
121 fn keeps_rule_with_unknown_declaration() {
122 assert_no_transform!(CssMinifierFeature::RemoveInertNodes, CssAtomSet, StyleSheet, "a { color: fnord }");
123 }
124
125 #[test]
126 fn keeps_rule_containing_a_statement_at_rule() {
127 assert_no_transform!(
128 CssMinifierFeature::RemoveInertNodes,
129 CssAtomSet,
130 StyleSheet,
131 "@media screen { @layer a; }"
132 );
133 }
134
135 #[test]
136 fn keeps_empty_layer_rule() {
137 assert_no_transform!(CssMinifierFeature::RemoveInertNodes, CssAtomSet, StyleSheet, "@layer a {}");
138 }
139
140 #[test]
141 fn keeps_empty_unknown_at_rule() {
142 assert_no_transform!(CssMinifierFeature::RemoveInertNodes, CssAtomSet, StyleSheet, "@fnord {}");
143 }
144
145 #[test]
146 fn keeps_rule_containing_an_unknown_at_rule() {
147 assert_no_transform!(CssMinifierFeature::RemoveInertNodes, CssAtomSet, StyleSheet, "a { @fnord {} }");
148 }
149}