#![deny(clippy::all)]
#![allow(clippy::needless_update)]
#![allow(non_local_definitions)]
pub use std::fmt::Result;
use std::{borrow::Cow, str, str::from_utf8};
use serde::{Deserialize, Serialize};
use swc_common::{BytePos, Span, Spanned, DUMMY_SP};
use swc_css_ast::*;
use swc_css_codegen_macros::emitter;
use swc_css_utils::serialize_ident;
use writer::CssWriter;
pub use self::emit::*;
use self::{ctx::Ctx, list::ListFormat};
#[macro_use]
mod macros;
mod ctx;
mod emit;
mod list;
pub mod writer;
#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CodegenConfig {
#[serde(default)]
pub minify: bool,
}
#[derive(Debug)]
pub struct CodeGenerator<W>
where
W: CssWriter,
{
wr: W,
config: CodegenConfig,
ctx: Ctx,
}
impl<W> CodeGenerator<W>
where
W: CssWriter,
{
pub fn new(wr: W, config: CodegenConfig) -> Self {
CodeGenerator {
wr,
config,
ctx: Default::default(),
}
}
#[emitter]
fn emit_stylesheet(&mut self, n: &Stylesheet) -> Result {
self.emit_list(
&n.rules,
if self.config.minify {
ListFormat::NotDelimited
} else {
ListFormat::NotDelimited | ListFormat::MultiLine
},
)?;
}
#[emitter]
fn emit_rule(&mut self, n: &Rule) -> Result {
match n {
Rule::QualifiedRule(n) => emit!(self, n),
Rule::AtRule(n) => emit!(self, n),
Rule::ListOfComponentValues(n) => {
emit!(
&mut *self.with_ctx(Ctx {
in_list_of_component_values: true,
..self.ctx
}),
n
)
}
}
}
#[emitter]
fn emit_qualified_rule(&mut self, n: &QualifiedRule) -> Result {
emit!(self, n.prelude);
emit!(self, n.block);
}
#[emitter]
fn emit_qualified_rule_prelude(&mut self, n: &QualifiedRulePrelude) -> Result {
match n {
QualifiedRulePrelude::SelectorList(n) => {
emit!(self, n);
formatting_space!(self);
}
QualifiedRulePrelude::RelativeSelectorList(n) => {
emit!(self, n);
formatting_space!(self);
}
QualifiedRulePrelude::ListOfComponentValues(n) => {
emit!(
&mut *self.with_ctx(Ctx {
in_list_of_component_values: true,
..self.ctx
}),
n
)
}
}
}
#[emitter]
fn emit_at_rule(&mut self, n: &AtRule) -> Result {
write_raw!(self, lo_span_offset!(n.span, 1), "@");
emit!(
&mut *self.with_ctx(Ctx {
allow_to_lowercase: true,
..self.ctx
}),
n.name
);
if let Some(prelude) = &n.prelude {
emit!(
&mut *self.with_ctx(Ctx {
in_single_line_selectors: true,
..self.ctx
}),
prelude
);
}
if n.block.is_some() {
match n.prelude.as_deref() {
Some(AtRulePrelude::ListOfComponentValues(_)) | None => {}
_ => {
formatting_space!(self);
}
}
emit!(self, n.block)
} else {
semi!(self);
}
}
#[emitter]
fn emit_at_rule_name(&mut self, n: &AtRuleName) -> Result {
match n {
AtRuleName::Ident(n) => emit!(self, n),
AtRuleName::DashedIdent(n) => emit!(self, n),
}
}
#[emitter]
fn emit_at_rule_prelude(&mut self, n: &AtRulePrelude) -> Result {
match n {
AtRulePrelude::CharsetPrelude(n) => {
space!(self);
emit!(self, n);
}
AtRulePrelude::PropertyPrelude(n) => {
space!(self);
emit!(self, n);
}
AtRulePrelude::CounterStylePrelude(n) => {
space!(self);
emit!(self, n);
}
AtRulePrelude::ColorProfilePrelude(n) => {
space!(self);
emit!(self, n);
}
AtRulePrelude::DocumentPrelude(n) => {
space!(self);
emit!(self, n);
}
AtRulePrelude::FontPaletteValuesPrelude(n) => {
space!(self);
emit!(self, n);
}
AtRulePrelude::FontFeatureValuesPrelude(n) => {
let need_space = !matches!(n.font_family.first(), Some(FamilyName::Str(_)));
if need_space {
space!(self);
} else {
formatting_space!(self);
}
emit!(self, n);
}
AtRulePrelude::NestPrelude(n) => {
space!(self);
emit!(self, n);
}
AtRulePrelude::KeyframesPrelude(n) => {
match n {
KeyframesName::Str(_) => {
formatting_space!(self);
}
KeyframesName::CustomIdent(_)
| KeyframesName::PseudoPrefix(_)
| KeyframesName::PseudoFunction(_) => {
space!(self);
}
}
emit!(self, n);
}
AtRulePrelude::ImportPrelude(n) => {
match &*n.href {
ImportHref::Url(_) => {
space!(self);
}
ImportHref::Str(_) => {
formatting_space!(self);
}
}
emit!(self, n);
}
AtRulePrelude::NamespacePrelude(n) => emit!(self, n),
AtRulePrelude::MediaPrelude(n) => {
let need_space = match n.queries.first() {
Some(media_query)
if media_query.modifier.is_none() && media_query.media_type.is_none() =>
{
match media_query.condition.as_deref() {
Some(MediaConditionType::All(media_condition)) => !matches!(
media_condition.conditions.first(),
Some(MediaConditionAllType::MediaInParens(
MediaInParens::MediaCondition(_)
)) | Some(MediaConditionAllType::MediaInParens(
MediaInParens::Feature(_)
)) | Some(MediaConditionAllType::MediaInParens(
MediaInParens::GeneralEnclosed(GeneralEnclosed::SimpleBlock(_))
))
),
_ => true,
}
}
_ => true,
};
if need_space {
space!(self);
} else {
formatting_space!(self);
}
emit!(self, n);
}
AtRulePrelude::SupportsPrelude(n) => {
let need_space = !matches!(
n.conditions.first(),
Some(SupportsConditionType::SupportsInParens(
SupportsInParens::SupportsCondition(_)
)) | Some(SupportsConditionType::SupportsInParens(
SupportsInParens::Feature(SupportsFeature::Declaration(_))
)) | Some(SupportsConditionType::SupportsInParens(
SupportsInParens::GeneralEnclosed(GeneralEnclosed::SimpleBlock(_)),
))
);
if need_space {
space!(self);
} else {
formatting_space!(self);
}
emit!(self, n);
}
AtRulePrelude::PagePrelude(n) => {
match n.selectors.first() {
Some(page_selector) if page_selector.page_type.is_none() => {
formatting_space!(self);
}
_ => {
space!(self);
}
}
emit!(self, n);
}
AtRulePrelude::LayerPrelude(n) => {
space!(self);
emit!(self, n);
}
AtRulePrelude::ContainerPrelude(n) => {
let need_space = match n.name {
Some(_) => true,
_ => !matches!(
n.query.queries.first(),
Some(ContainerQueryType::QueryInParens(
QueryInParens::ContainerQuery(_,)
)) | Some(ContainerQueryType::QueryInParens(
QueryInParens::SizeFeature(_)
)) | Some(ContainerQueryType::QueryInParens(
QueryInParens::GeneralEnclosed(GeneralEnclosed::SimpleBlock(_)),
))
),
};
if need_space {
space!(self);
} else {
formatting_space!(self);
}
emit!(self, n);
}
AtRulePrelude::CustomMediaPrelude(n) => {
space!(self);
emit!(self, n);
}
AtRulePrelude::ListOfComponentValues(n) => {
emit!(
&mut *self.with_ctx(Ctx {
in_list_of_component_values: true,
..self.ctx
}),
n
)
}
AtRulePrelude::ScopePrelude(n) => {
emit!(self, n);
}
}
}
#[emitter]
fn emit_list_of_component_values(&mut self, n: &ListOfComponentValues) -> Result {
self.emit_list_of_component_values_inner(
&n.children,
ListFormat::SpaceDelimited | ListFormat::SingleLine,
)?;
}
#[emitter]
fn emit_import_prelude(&mut self, n: &ImportPrelude) -> Result {
emit!(self, n.href);
if n.layer_name.is_some() || n.import_conditions.is_some() {
formatting_space!(self);
}
if let Some(layer_name) = &n.layer_name {
emit!(self, layer_name);
if n.import_conditions.is_some() {
if let ImportLayerName::Ident(_) = &**layer_name {
space!(self);
} else {
formatting_space!(self);
}
}
}
emit!(self, n.import_conditions);
}
#[emitter]
fn emit_import_prelude_href(&mut self, n: &ImportHref) -> Result {
match n {
ImportHref::Url(n) => emit!(self, n),
ImportHref::Str(n) => emit!(self, n),
}
}
#[emitter]
fn emit_import_layer_name(&mut self, n: &ImportLayerName) -> Result {
match n {
ImportLayerName::Ident(n) => emit!(self, n),
ImportLayerName::Function(n) if n.value.is_empty() => {
emit!(
self,
AtRuleName::Ident(swc_css_ast::Ident {
span: n.span,
value: "layer".into(),
raw: None
})
)
}
ImportLayerName::Function(n) => {
emit!(self, n)
}
}
}
#[emitter]
fn emit_import_conditions(&mut self, n: &ImportConditions) -> Result {
if let Some(supports) = &n.supports {
emit!(self, supports);
if n.media.is_some() {
formatting_space!(self);
}
}
if let Some(media) = &n.media {
emit!(self, media);
}
}
#[emitter]
fn emit_keyframes_name(&mut self, n: &KeyframesName) -> Result {
match n {
KeyframesName::CustomIdent(n) => emit!(self, n),
KeyframesName::Str(n) => emit!(self, n),
KeyframesName::PseudoFunction(n) => emit!(self, n),
KeyframesName::PseudoPrefix(n) => emit!(self, n),
}
}
#[emitter]
fn emit_keyframes_pseudo_function(&mut self, n: &KeyframesPseudoFunction) -> Result {
write_raw!(self, ":");
emit!(self, n.pseudo);
write_raw!(self, "(");
emit!(self, n.name);
write_raw!(self, ")");
}
#[emitter]
fn emit_keyframes_pseudo_prefix(&mut self, n: &KeyframesPseudoPrefix) -> Result {
write_raw!(self, ":");
emit!(self, n.pseudo);
space!(self);
emit!(self, n.name);
}
#[emitter]
fn emit_keyframe_block(&mut self, n: &KeyframeBlock) -> Result {
self.emit_list(&n.prelude, ListFormat::CommaDelimited)?;
formatting_space!(self);
emit!(self, n.block);
}
#[emitter]
fn emit_keyframe_selector(&mut self, n: &KeyframeSelector) -> Result {
match n {
KeyframeSelector::Ident(n) => emit!(
&mut *self.with_ctx(Ctx {
allow_to_lowercase: true,
..self.ctx
}),
n
),
KeyframeSelector::Percentage(n) => emit!(self, n),
}
}
#[emitter]
fn emit_font_feature_values_prelude(&mut self, n: &FontFeatureValuesPrelude) -> Result {
self.emit_list(&n.font_family, ListFormat::CommaDelimited)?;
}
#[emitter]
fn emit_layer_name(&mut self, n: &LayerName) -> Result {
self.emit_list(&n.name, ListFormat::DotDelimited)?;
}
#[emitter]
fn emit_layer_name_list(&mut self, n: &LayerNameList) -> Result {
self.emit_list(&n.name_list, ListFormat::CommaDelimited)?;
}
#[emitter]
fn emit_layer_prelude(&mut self, n: &LayerPrelude) -> Result {
match n {
LayerPrelude::Name(n) => emit!(self, n),
LayerPrelude::NameList(n) => emit!(self, n),
}
}
#[emitter]
fn emit_media_query_list(&mut self, n: &MediaQueryList) -> Result {
self.emit_list(&n.queries, ListFormat::CommaDelimited)?;
}
#[emitter]
fn emit_media_query(&mut self, n: &MediaQuery) -> Result {
if n.modifier.is_some() {
emit!(
&mut *self.with_ctx(Ctx {
allow_to_lowercase: true,
..self.ctx
}),
n.modifier
);
space!(self);
}
if n.media_type.is_some() {
emit!(
&mut *self.with_ctx(Ctx {
allow_to_lowercase: true,
..self.ctx
}),
n.media_type
);
if n.condition.is_some() {
space!(self);
write_raw!(self, "and");
space!(self);
}
}
if n.condition.is_some() {
emit!(self, n.condition);
}
}
#[emitter]
fn emit_media_type(&mut self, n: &MediaType) -> Result {
match n {
MediaType::Ident(n) => emit!(self, n),
}
}
#[emitter]
fn emit_media_condition_type(&mut self, n: &MediaConditionType) -> Result {
match n {
MediaConditionType::All(n) => emit!(self, n),
MediaConditionType::WithoutOr(n) => emit!(self, n),
}
}
#[emitter]
fn emit_media_condition(&mut self, n: &MediaCondition) -> Result {
self.emit_list(
&n.conditions,
if self.config.minify {
ListFormat::NotDelimited
} else {
ListFormat::SpaceDelimited
},
)?;
}
#[emitter]
fn emit_media_condition_without_or(&mut self, n: &MediaConditionWithoutOr) -> Result {
self.emit_list(
&n.conditions,
if self.config.minify {
ListFormat::NotDelimited
} else {
ListFormat::SpaceDelimited
},
)?;
}
#[emitter]
fn emit_media_condition_all_type(&mut self, n: &MediaConditionAllType) -> Result {
match n {
MediaConditionAllType::Not(n) => emit!(self, n),
MediaConditionAllType::And(n) => emit!(self, n),
MediaConditionAllType::Or(n) => emit!(self, n),
MediaConditionAllType::MediaInParens(n) => emit!(self, n),
}
}
#[emitter]
fn emit_media_condition_without_or_type(&mut self, n: &MediaConditionWithoutOrType) -> Result {
match n {
MediaConditionWithoutOrType::Not(n) => emit!(self, n),
MediaConditionWithoutOrType::And(n) => emit!(self, n),
MediaConditionWithoutOrType::MediaInParens(n) => emit!(self, n),
}
}
#[emitter]
fn emit_media_not(&mut self, n: &MediaNot) -> Result {
write_raw!(self, "not");
space!(self);
emit!(self, n.condition);
}
#[emitter]
fn emit_media_and(&mut self, n: &MediaAnd) -> Result {
write_raw!(self, "and");
space!(self);
emit!(self, n.condition);
}
#[emitter]
fn emit_media_or(&mut self, n: &MediaOr) -> Result {
write_raw!(self, "or");
space!(self);
emit!(self, n.condition);
}
#[emitter]
fn emit_media_in_parens(&mut self, n: &MediaInParens) -> Result {
match n {
MediaInParens::MediaCondition(n) => {
write_raw!(self, lo_span_offset!(n.span, 1), "(");
emit!(self, n);
write_raw!(self, hi_span_offset!(n.span, 1), ")");
}
MediaInParens::Feature(n) => emit!(self, n),
MediaInParens::GeneralEnclosed(n) => emit!(self, n),
}
}
#[emitter]
fn emit_media_feature(&mut self, n: &MediaFeature) -> Result {
let span = match n {
MediaFeature::Plain(n) => n.span,
MediaFeature::Boolean(n) => n.span,
MediaFeature::Range(n) => n.span,
MediaFeature::RangeInterval(n) => n.span,
};
write_raw!(self, lo_span_offset!(span, 1), "(");
match n {
MediaFeature::Plain(n) => emit!(self, n),
MediaFeature::Boolean(n) => emit!(self, n),
MediaFeature::Range(n) => emit!(self, n),
MediaFeature::RangeInterval(n) => emit!(self, n),
}
write_raw!(self, hi_span_offset!(span, 1), ")");
}
#[emitter]
fn emit_media_feature_name(&mut self, n: &MediaFeatureName) -> Result {
match n {
MediaFeatureName::Ident(n) => emit!(self, n),
MediaFeatureName::ExtensionName(n) => emit!(self, n),
}
}
#[emitter]
fn emit_media_feature_value(&mut self, n: &MediaFeatureValue) -> Result {
match n {
MediaFeatureValue::Number(n) => emit!(self, n),
MediaFeatureValue::Dimension(n) => emit!(self, n),
MediaFeatureValue::Ident(n) => emit!(self, n),
MediaFeatureValue::Ratio(n) => emit!(self, n),
MediaFeatureValue::Function(n) => emit!(self, n),
}
}
#[emitter]
fn emit_media_feature_plain(&mut self, n: &MediaFeaturePlain) -> Result {
emit!(self, n.name);
write_raw!(self, ":");
formatting_space!(self);
emit!(self, n.value);
}
#[emitter]
fn emit_media_feature_boolean(&mut self, n: &MediaFeatureBoolean) -> Result {
emit!(self, n.name);
}
#[emitter]
fn emit_media_feature_range(&mut self, n: &MediaFeatureRange) -> Result {
emit!(self, n.left);
formatting_space!(self);
write_raw!(self, n.span, n.comparison.as_str());
formatting_space!(self);
emit!(self, n.right);
}
#[emitter]
fn emit_media_feature_range_interval(&mut self, n: &MediaFeatureRangeInterval) -> Result {
emit!(self, n.left);
formatting_space!(self);
write_raw!(self, n.span, n.left_comparison.as_str());
formatting_space!(self);
emit!(self, n.name);
formatting_space!(self);
write_raw!(self, n.span, n.right_comparison.as_str());
formatting_space!(self);
emit!(self, n.right);
}
#[emitter]
fn emit_supports_condition(&mut self, n: &SupportsCondition) -> Result {
self.emit_list(
&n.conditions,
if self.config.minify {
ListFormat::NotDelimited
} else {
ListFormat::SpaceDelimited
},
)?;
}
#[emitter]
fn emit_supports_condition_type(&mut self, n: &SupportsConditionType) -> Result {
match n {
SupportsConditionType::Not(n) => emit!(self, n),
SupportsConditionType::And(n) => emit!(self, n),
SupportsConditionType::Or(n) => emit!(self, n),
SupportsConditionType::SupportsInParens(n) => emit!(self, n),
}
}
#[emitter]
fn emit_supports_not(&mut self, n: &SupportsNot) -> Result {
write_raw!(self, "not");
space!(self);
emit!(self, n.condition);
}
#[emitter]
fn emit_supports_and(&mut self, n: &SupportsAnd) -> Result {
write_raw!(self, "and");
space!(self);
emit!(self, n.condition);
}
#[emitter]
fn emit_support_or(&mut self, n: &SupportsOr) -> Result {
write_raw!(self, "or");
space!(self);
emit!(self, n.condition);
}
#[emitter]
fn emit_supports_in_parens(&mut self, n: &SupportsInParens) -> Result {
match n {
SupportsInParens::SupportsCondition(n) => {
write_raw!(self, lo_span_offset!(n.span, 1), "(");
emit!(self, n);
write_raw!(self, hi_span_offset!(n.span, 1), ")");
}
SupportsInParens::Feature(n) => emit!(self, n),
SupportsInParens::GeneralEnclosed(n) => emit!(self, n),
}
}
#[emitter]
fn emit_supports_feature(&mut self, n: &SupportsFeature) -> Result {
match n {
SupportsFeature::Declaration(n) => {
write_raw!(self, lo_span_offset!(n.span, 1), "(");
emit!(self, n);
write_raw!(self, hi_span_offset!(n.span, 1), ")");
}
SupportsFeature::Function(n) => emit!(self, n),
}
}
#[emitter]
fn emit_general_enclosed(&mut self, n: &GeneralEnclosed) -> Result {
match n {
GeneralEnclosed::Function(n) => emit!(self, n),
GeneralEnclosed::SimpleBlock(n) => emit!(self, n),
}
}
#[emitter]
fn emit_page_selector_list(&mut self, n: &PageSelectorList) -> Result {
self.emit_list(&n.selectors, ListFormat::CommaDelimited)?;
}
#[emitter]
fn emit_page_selector(&mut self, n: &PageSelector) -> Result {
if let Some(page_type) = &n.page_type {
emit!(self, page_type);
}
if let Some(pseudos) = &n.pseudos {
self.emit_list(pseudos, ListFormat::NotDelimited)?;
}
}
#[emitter]
fn emit_page_selector_type(&mut self, n: &PageSelectorType) -> Result {
emit!(self, n.value);
}
#[emitter]
fn emit_page_selector_pseudo(&mut self, n: &PageSelectorPseudo) -> Result {
write_raw!(self, ":");
emit!(
&mut *self.with_ctx(Ctx {
allow_to_lowercase: true,
..self.ctx
}),
n.value
);
}
#[emitter]
fn emit_namespace_prelude(&mut self, n: &NamespacePrelude) -> Result {
let has_prefix = n.prefix.is_some();
let is_uri_url = match &*n.uri {
NamespacePreludeUri::Url(_) => true,
NamespacePreludeUri::Str(_) => false,
};
if has_prefix || is_uri_url {
space!(self);
} else {
formatting_space!(self);
}
if has_prefix {
emit!(self, n.prefix);
if is_uri_url {
space!(self);
} else {
formatting_space!(self);
}
}
emit!(self, n.uri);
}
#[emitter]
fn emit_namespace_prelude_uri(&mut self, n: &NamespacePreludeUri) -> Result {
match n {
NamespacePreludeUri::Url(n) => emit!(self, n),
NamespacePreludeUri::Str(n) => emit!(self, n),
}
}
#[emitter]
fn emit_document_prelude(&mut self, n: &DocumentPrelude) -> Result {
self.emit_list(&n.matching_functions, ListFormat::CommaDelimited)?;
}
#[emitter]
fn emit_document_prelude_matching_function(
&mut self,
n: &DocumentPreludeMatchingFunction,
) -> Result {
match n {
DocumentPreludeMatchingFunction::Url(n) => emit!(self, n),
DocumentPreludeMatchingFunction::Function(n) => emit!(self, n),
}
}
#[emitter]
fn emit_container_condition(&mut self, n: &ContainerCondition) -> Result {
if let Some(name) = &n.name {
emit!(self, name);
space!(self);
}
emit!(self, n.query);
}
#[emitter]
fn emit_container_name(&mut self, n: &ContainerName) -> Result {
match n {
ContainerName::CustomIdent(n) => emit!(self, n),
}
}
#[emitter]
fn emit_container_query(&mut self, n: &ContainerQuery) -> Result {
self.emit_list(
&n.queries,
if self.config.minify {
ListFormat::NotDelimited
} else {
ListFormat::SpaceDelimited
},
)?;
}
#[emitter]
fn emit_container_query_type(&mut self, n: &ContainerQueryType) -> Result {
match n {
ContainerQueryType::Not(n) => emit!(self, n),
ContainerQueryType::And(n) => emit!(self, n),
ContainerQueryType::Or(n) => emit!(self, n),
ContainerQueryType::QueryInParens(n) => emit!(self, n),
}
}
#[emitter]
fn emit_container_query_not(&mut self, n: &ContainerQueryNot) -> Result {
write_raw!(self, "not");
space!(self);
emit!(self, n.query);
}
#[emitter]
fn emit_container_query_and(&mut self, n: &ContainerQueryAnd) -> Result {
write_raw!(self, "and");
space!(self);
emit!(self, n.query);
}
#[emitter]
fn emit_container_query_or(&mut self, n: &ContainerQueryOr) -> Result {
write_raw!(self, "or");
space!(self);
emit!(self, n.query);
}
#[emitter]
fn emit_query_in_parens(&mut self, n: &QueryInParens) -> Result {
match n {
QueryInParens::ContainerQuery(n) => {
write_raw!(self, lo_span_offset!(n.span, 1), "(");
emit!(self, n);
write_raw!(self, hi_span_offset!(n.span, 1), ")");
}
QueryInParens::SizeFeature(n) => emit!(self, n),
QueryInParens::GeneralEnclosed(n) => emit!(self, n),
}
}
#[emitter]
fn emit_size_feature(&mut self, n: &SizeFeature) -> Result {
let span = match n {
SizeFeature::Plain(n) => n.span,
SizeFeature::Boolean(n) => n.span,
SizeFeature::Range(n) => n.span,
SizeFeature::RangeInterval(n) => n.span,
};
write_raw!(self, lo_span_offset!(span, 1), "(");
match n {
SizeFeature::Plain(n) => emit!(self, n),
SizeFeature::Boolean(n) => emit!(self, n),
SizeFeature::Range(n) => emit!(self, n),
SizeFeature::RangeInterval(n) => emit!(self, n),
}
write_raw!(self, hi_span_offset!(span, 1), ")");
}
#[emitter]
fn emit_size_feature_name(&mut self, n: &SizeFeatureName) -> Result {
match n {
SizeFeatureName::Ident(n) => emit!(self, n),
}
}
#[emitter]
fn emit_size_feature_value(&mut self, n: &SizeFeatureValue) -> Result {
match n {
SizeFeatureValue::Number(n) => emit!(self, n),
SizeFeatureValue::Dimension(n) => emit!(self, n),
SizeFeatureValue::Ident(n) => emit!(self, n),
SizeFeatureValue::Ratio(n) => emit!(self, n),
SizeFeatureValue::Function(n) => emit!(self, n),
}
}
#[emitter]
fn emit_size_feature_plain(&mut self, n: &SizeFeaturePlain) -> Result {
emit!(
&mut *self.with_ctx(Ctx {
allow_to_lowercase: true,
..self.ctx
}),
n.name
);
write_raw!(self, ":");
formatting_space!(self);
emit!(self, n.value);
}
#[emitter]
fn emit_size_feature_boolean(&mut self, n: &SizeFeatureBoolean) -> Result {
emit!(
&mut *self.with_ctx(Ctx {
allow_to_lowercase: true,
..self.ctx
}),
n.name
);
}
#[emitter]
fn emit_size_feature_range(&mut self, n: &SizeFeatureRange) -> Result {
emit!(self, n.left);
formatting_space!(self);
write_raw!(self, n.span, n.comparison.as_str());
formatting_space!(self);
emit!(self, n.right);
}
#[emitter]
fn emit_size_feature_range_interval(&mut self, n: &SizeFeatureRangeInterval) -> Result {
emit!(self, n.left);
formatting_space!(self);
write_raw!(self, n.span, n.left_comparison.as_str());
formatting_space!(self);
emit!(
&mut *self.with_ctx(Ctx {
allow_to_lowercase: true,
..self.ctx
}),
n.name
);
formatting_space!(self);
write_raw!(self, n.span, n.right_comparison.as_str());
formatting_space!(self);
emit!(self, n.right);
}
#[emitter]
fn emit_custom_media_query(&mut self, n: &CustomMediaQuery) -> Result {
emit!(self, n.name);
space!(self);
emit!(self, n.media);
}
#[emitter]
fn emit_custom_media_query_media_type(&mut self, n: &CustomMediaQueryMediaType) -> Result {
match n {
CustomMediaQueryMediaType::MediaQueryList(n) => emit!(self, n),
CustomMediaQueryMediaType::Ident(n) => emit!(self, n),
}
}
fn emit_list_of_component_values_inner(
&mut self,
nodes: &[ComponentValue],
format: ListFormat,
) -> Result {
let iter = nodes.iter();
let len = nodes.len();
for (idx, node) in iter.enumerate() {
emit!(self, node);
if self.ctx.in_list_of_component_values {
continue;
}
let is_current_preserved_token = matches!(node, ComponentValue::PreservedToken(_));
let next = nodes.get(idx + 1);
let is_next_preserved_token = matches!(next, Some(ComponentValue::PreservedToken(_)));
if idx != len - 1 && !is_current_preserved_token && !is_next_preserved_token {
let need_delim = match node {
ComponentValue::SimpleBlock(_)
| ComponentValue::Function(_)
| ComponentValue::Delimiter(_)
| ComponentValue::Str(_)
| ComponentValue::Url(_)
| ComponentValue::Percentage(_)
| ComponentValue::LengthPercentage(_)
| ComponentValue::FrequencyPercentage(_)
| ComponentValue::AnglePercentage(_)
| ComponentValue::TimePercentage(_) => match next {
Some(ComponentValue::Delimiter(delimiter))
if matches!(
**delimiter,
Delimiter {
value: DelimiterValue::Comma,
..
}
) =>
{
false
}
_ => !self.config.minify,
},
ComponentValue::Color(color)
if matches!(
**color,
Color::AbsoluteColorBase(AbsoluteColorBase::Function(_))
| Color::Function(_)
) =>
{
match next {
Some(ComponentValue::Delimiter(delimiter))
if matches!(
**delimiter,
Delimiter {
value: DelimiterValue::Comma,
..
}
) =>
{
false
}
_ => !self.config.minify,
}
}
ComponentValue::Ident(_) | ComponentValue::DashedIdent(_) => match next {
Some(ComponentValue::SimpleBlock(simple_block)) => {
if simple_block.name.token == Token::LParen {
true
} else {
!self.config.minify
}
}
Some(ComponentValue::Color(color))
if matches!(
**color,
Color::AbsoluteColorBase(AbsoluteColorBase::HexColor(_),)
) =>
{
!self.config.minify
}
Some(ComponentValue::Str(_)) => !self.config.minify,
Some(ComponentValue::Delimiter(_)) => false,
Some(ComponentValue::Number(n)) => {
if self.config.minify {
let minified = minify_numeric(n.value);
!minified.starts_with('.')
} else {
true
}
}
Some(ComponentValue::Dimension(dimension)) => {
if self.config.minify {
let value = match &**dimension {
Dimension::Length(i) => i.value.value,
Dimension::Angle(i) => i.value.value,
Dimension::Time(i) => i.value.value,
Dimension::Frequency(i) => i.value.value,
Dimension::Resolution(i) => i.value.value,
Dimension::Flex(i) => i.value.value,
Dimension::UnknownDimension(i) => i.value.value,
};
let minified = minify_numeric(value);
!minified.starts_with('.')
} else {
true
}
}
Some(component_value) if self.config.minify => {
if let Some(minified) = match component_value {
ComponentValue::LengthPercentage(p) => {
p.as_length().map(|l| l.value.value)
}
ComponentValue::FrequencyPercentage(p) => {
p.as_frequency().map(|f| f.value.value)
}
ComponentValue::AnglePercentage(p) => {
p.as_angle().map(|a| a.value.value)
}
ComponentValue::TimePercentage(p) => {
p.as_time().map(|t| t.value.value)
}
_ => None,
}
.map(minify_numeric)
{
!minified.starts_with('.')
} else {
true
}
}
_ => true,
},
_ => match next {
Some(ComponentValue::SimpleBlock(_)) => !self.config.minify,
Some(ComponentValue::Color(color))
if matches!(
&**color,
Color::AbsoluteColorBase(AbsoluteColorBase::HexColor(_))
) =>
{
!self.config.minify
}
Some(ComponentValue::Delimiter(_)) => false,
_ => true,
},
};
if need_delim {
self.write_delim(format)?;
}
}
}
Ok(())
}
#[emitter]
fn emit_function(&mut self, n: &Function) -> Result {
emit!(
&mut *self.with_ctx(Ctx {
allow_to_lowercase: true,
..self.ctx
}),
n.name
);
write_raw!(self, "(");
self.emit_list_of_component_values_inner(
&n.value,
ListFormat::SpaceDelimited | ListFormat::SingleLine,
)?;
write_raw!(self, ")");
}
#[emitter]
fn emit_function_name(&mut self, n: &FunctionName) -> Result {
match n {
FunctionName::Ident(n) => emit!(self, n),
FunctionName::DashedIdent(n) => emit!(self, n),
}
}
#[emitter]
fn emit_color_profile_name(&mut self, n: &ColorProfileName) -> Result {
match n {
ColorProfileName::Ident(n) => emit!(self, n),
ColorProfileName::DashedIdent(n) => emit!(self, n),
}
}
#[emitter]
fn emit_str(&mut self, n: &Str) -> Result {
if self.config.minify {
let minified = minify_string(&n.value);
write_str!(self, n.span, &minified);
} else if let Some(raw) = &n.raw {
write_str!(self, n.span, raw);
} else {
let value = serialize_string(&n.value);
write_str!(self, n.span, &value);
}
}
#[emitter]
fn emit_simple_block(&mut self, n: &SimpleBlock) -> Result {
let (starting, ending) = match n.name.token {
Token::LBracket => ("[", "]"),
Token::LParen => ("(", ")"),
Token::LBrace => ("{", "}"),
_ => {
unreachable!();
}
};
write_raw!(self, lo_span_offset!(n.span, 1), starting);
let len = n.value.len();
for (idx, node) in n.value.iter().enumerate() {
match node {
ComponentValue::ListOfComponentValues(_) | ComponentValue::Declaration(_) => {
if idx == 0 {
formatting_newline!(self);
}
increase_indent!(self);
}
ComponentValue::AtRule(_)
| ComponentValue::QualifiedRule(_)
| ComponentValue::KeyframeBlock(_) => {
formatting_newline!(self);
increase_indent!(self);
}
_ => {}
}
match node {
ComponentValue::ListOfComponentValues(node) => {
emit!(
&mut *self.with_ctx(Ctx {
in_list_of_component_values: true,
..self.ctx
}),
node
);
}
_ => {
emit!(self, node);
}
}
match node {
ComponentValue::AtRule(_) | ComponentValue::QualifiedRule(_) => {
formatting_newline!(self);
decrease_indent!(self);
}
ComponentValue::Declaration(_) => {
if idx != len - 1 {
semi!(self);
} else {
formatting_semi!(self);
}
formatting_newline!(self);
decrease_indent!(self);
}
ComponentValue::ListOfComponentValues(_) => {
decrease_indent!(self);
}
ComponentValue::KeyframeBlock(_) => {
if idx == len - 1 {
formatting_newline!(self);
}
decrease_indent!(self);
}
_ => {
if !self.ctx.in_list_of_component_values && ending == "]" && idx != len - 1 {
space!(self);
}
}
}
}
write_raw!(self, hi_span_offset!(n.span, 1), ending);
}
#[emitter]
fn emit_component_value(&mut self, n: &ComponentValue) -> Result {
match n {
ComponentValue::PreservedToken(n) => emit!(self, n),
ComponentValue::Function(n) => emit!(self, n),
ComponentValue::SimpleBlock(n) => emit!(self, n),
ComponentValue::ListOfComponentValues(n) => emit!(self, n),
ComponentValue::QualifiedRule(n) => emit!(self, n),
ComponentValue::AtRule(n) => emit!(self, n),
ComponentValue::KeyframeBlock(n) => emit!(self, n),
ComponentValue::Ident(n) => emit!(self, n),
ComponentValue::DashedIdent(n) => emit!(self, n),
ComponentValue::Str(n) => emit!(self, n),
ComponentValue::Url(n) => emit!(self, n),
ComponentValue::Integer(n) => emit!(self, n),
ComponentValue::Number(n) => emit!(self, n),
ComponentValue::Percentage(n) => emit!(self, n),
ComponentValue::Dimension(n) => emit!(self, n),
ComponentValue::LengthPercentage(n) => emit!(self, n),
ComponentValue::FrequencyPercentage(n) => emit!(self, n),
ComponentValue::AnglePercentage(n) => emit!(self, n),
ComponentValue::TimePercentage(n) => emit!(self, n),
ComponentValue::Ratio(n) => emit!(self, n),
ComponentValue::UnicodeRange(n) => emit!(self, n),
ComponentValue::Color(n) => emit!(self, n),
ComponentValue::AlphaValue(n) => emit!(self, n),
ComponentValue::Hue(n) => emit!(self, n),
ComponentValue::CmykComponent(n) => emit!(self, n),
ComponentValue::Delimiter(n) => emit!(self, n),
ComponentValue::CalcSum(n) => emit!(self, n),
ComponentValue::ComplexSelector(n) => emit!(self, n),
ComponentValue::LayerName(n) => emit!(self, n),
ComponentValue::Declaration(n) => emit!(self, n),
ComponentValue::SupportsCondition(n) => emit!(self, n),
ComponentValue::IdSelector(n) => emit!(self, n),
}
}
#[emitter]
fn emit_style_block(&mut self, n: &StyleBlock) -> Result {
match n {
StyleBlock::ListOfComponentValues(n) => {
emit!(
&mut *self.with_ctx(Ctx {
in_list_of_component_values: true,
..self.ctx
}),
n
)
}
StyleBlock::AtRule(n) => emit!(self, n),
StyleBlock::Declaration(n) => emit!(self, n),
StyleBlock::QualifiedRule(n) => emit!(self, n),
}
}
#[emitter]
fn emit_declaration_block_item(&mut self, n: &DeclarationOrAtRule) -> Result {
match n {
DeclarationOrAtRule::Declaration(n) => emit!(self, n),
DeclarationOrAtRule::AtRule(n) => emit!(self, n),
DeclarationOrAtRule::ListOfComponentValues(n) => {
emit!(
&mut *self.with_ctx(Ctx {
in_list_of_component_values: true,
..self.ctx
}),
n
)
}
}
}
#[emitter]
fn emit_declaration(&mut self, n: &Declaration) -> Result {
emit!(
&mut *self.with_ctx(Ctx {
allow_to_lowercase: true,
..self.ctx
}),
n.name
);
write_raw!(self, ":");
let is_custom_property = match n.name {
DeclarationName::DashedIdent(_) => true,
DeclarationName::Ident(_) => false,
};
if is_custom_property {
match n.value.first() {
None => {
space!(self);
}
_ => {
formatting_space!(self);
}
};
} else {
formatting_space!(self);
}
if is_custom_property {
self.with_ctx(Ctx {
in_list_of_component_values: true,
..self.ctx
})
.emit_list(&n.value, ListFormat::NotDelimited)?;
} else {
self.emit_list_of_component_values_inner(
&n.value,
ListFormat::SpaceDelimited | ListFormat::SingleLine,
)?;
}
if n.important.is_some() {
if !is_custom_property {
formatting_space!(self);
}
emit!(self, n.important);
}
}
#[emitter]
fn emit_declaration_name(&mut self, n: &DeclarationName) -> Result {
match n {
DeclarationName::Ident(n) => emit!(self, n),
DeclarationName::DashedIdent(n) => emit!(self, n),
}
}
#[emitter]
fn emit_important_flag(&mut self, n: &ImportantFlag) -> Result {
write_raw!(self, lo_span_offset!(n.span, 1), "!");
if self.config.minify {
emit!(
&mut *self.with_ctx(Ctx {
allow_to_lowercase: true,
..self.ctx
}),
n.value
);
} else {
emit!(self, n.value);
}
}
#[emitter]
fn emit_ident(&mut self, n: &Ident) -> Result {
let value = if self.ctx.allow_to_lowercase && self.config.minify {
Cow::Owned(n.value.to_ascii_lowercase())
} else {
Cow::Borrowed(&n.value)
};
let serialized = serialize_ident(&value, self.config.minify);
if self.ctx.is_dimension_unit {
write_raw!(self, n.span, &serialize_dimension_unit(&serialized));
} else {
write_raw!(self, n.span, &serialized);
}
}
#[emitter]
fn emit_custom_ident(&mut self, n: &CustomIdent) -> Result {
let serialized = serialize_ident(&n.value, self.config.minify);
write_raw!(self, n.span, &serialized);
}
#[emitter]
fn emit_dashed_ident(&mut self, n: &DashedIdent) -> Result {
write_raw!(self, lo_span_offset!(n.span, 2), "--");
let serialized = serialize_ident(&n.value, self.config.minify);
write_raw!(self, n.span, &serialized);
}
#[emitter]
fn emit_extension_name(&mut self, n: &ExtensionName) -> Result {
let serialized = serialize_ident(&n.value, self.config.minify);
write_raw!(self, n.span, &serialized);
}
#[emitter]
fn emit_custom_highlight_name(&mut self, n: &CustomHighlightName) -> Result {
let serialized = serialize_ident(&n.value, self.config.minify);
write_raw!(self, n.span, &serialized);
}
#[emitter]
fn emit_custom_property_name(&mut self, n: &CustomPropertyName) -> Result {
write_raw!(self, n.span, &n.value);
}
#[emitter]
fn emit_percentage(&mut self, n: &Percentage) -> Result {
emit!(self, n.value);
write_raw!(self, hi_span_offset!(n.span, 1), "%");
}
#[emitter]
fn emit_length_percentage(&mut self, n: &LengthPercentage) -> Result {
match n {
LengthPercentage::Length(n) => emit!(self, n),
LengthPercentage::Percentage(n) => emit!(self, n),
}
}
#[emitter]
fn emit_frequency_percentage(&mut self, n: &FrequencyPercentage) -> Result {
match n {
FrequencyPercentage::Frequency(n) => emit!(self, n),
FrequencyPercentage::Percentage(n) => emit!(self, n),
}
}
#[emitter]
fn emit_angle_percentage(&mut self, n: &AnglePercentage) -> Result {
match n {
AnglePercentage::Angle(n) => emit!(self, n),
AnglePercentage::Percentage(n) => emit!(self, n),
}
}
#[emitter]
fn emit_time_percentage(&mut self, n: &TimePercentage) -> Result {
match n {
TimePercentage::Time(n) => emit!(self, n),
TimePercentage::Percentage(n) => emit!(self, n),
}
}
#[emitter]
fn emit_dimension(&mut self, n: &Dimension) -> Result {
match n {
Dimension::Length(n) => emit!(self, n),
Dimension::Angle(n) => emit!(self, n),
Dimension::Time(n) => emit!(self, n),
Dimension::Frequency(n) => emit!(self, n),
Dimension::Resolution(n) => emit!(self, n),
Dimension::Flex(n) => emit!(self, n),
Dimension::UnknownDimension(n) => emit!(self, n),
}
}
#[emitter]
fn emit_length(&mut self, n: &Length) -> Result {
emit!(self, n.value);
emit!(
&mut *self.with_ctx(Ctx {
is_dimension_unit: true,
allow_to_lowercase: true,
..self.ctx
}),
n.unit
);
}
#[emitter]
fn emit_angle(&mut self, n: &Angle) -> Result {
emit!(self, n.value);
emit!(
&mut *self.with_ctx(Ctx {
is_dimension_unit: true,
allow_to_lowercase: true,
..self.ctx
}),
n.unit
);
}
#[emitter]
fn emit_time(&mut self, n: &Time) -> Result {
emit!(self, n.value);
emit!(
&mut *self.with_ctx(Ctx {
is_dimension_unit: true,
allow_to_lowercase: true,
..self.ctx
}),
n.unit
);
}
#[emitter]
fn emit_frequency(&mut self, n: &Frequency) -> Result {
emit!(self, n.value);
emit!(
&mut *self.with_ctx(Ctx {
is_dimension_unit: true,
allow_to_lowercase: true,
..self.ctx
}),
n.unit
);
}
#[emitter]
fn emit_resolution(&mut self, n: &Resolution) -> Result {
emit!(self, n.value);
emit!(
&mut *self.with_ctx(Ctx {
is_dimension_unit: true,
allow_to_lowercase: true,
..self.ctx
}),
n.unit
);
}
#[emitter]
fn emit_flex(&mut self, n: &Flex) -> Result {
emit!(self, n.value);
emit!(
&mut *self.with_ctx(Ctx {
is_dimension_unit: true,
allow_to_lowercase: true,
..self.ctx
}),
n.unit
);
}
#[emitter]
fn emit_unknown_dimension(&mut self, n: &UnknownDimension) -> Result {
emit!(self, n.value);
emit!(
&mut *self.with_ctx(Ctx {
is_dimension_unit: true,
allow_to_lowercase: true,
..self.ctx
}),
n.unit
);
}
#[emitter]
fn emit_integer(&mut self, n: &Integer) -> Result {
write_raw!(self, n.span, &n.value.to_string());
}
#[emitter]
fn emit_number(&mut self, n: &Number) -> Result {
if self.config.minify {
let minified = minify_numeric(n.value);
write_raw!(self, n.span, &minified);
} else if let Some(raw) = &n.raw {
write_raw!(self, n.span, raw);
} else {
write_raw!(self, n.span, &n.value.to_string());
}
}
#[emitter]
fn emit_ration(&mut self, n: &Ratio) -> Result {
emit!(self, n.left);
if let Some(right) = &n.right {
write_raw!(self, "/");
emit!(self, right);
}
}
#[emitter]
fn emit_color(&mut self, n: &Color) -> Result {
match n {
Color::AbsoluteColorBase(n) => emit!(self, n),
Color::CurrentColorOrSystemColor(n) => emit!(self, n),
Color::Function(n) => emit!(self, n),
}
}
#[emitter]
fn emit_absolute_color_base(&mut self, n: &AbsoluteColorBase) -> Result {
match n {
AbsoluteColorBase::HexColor(n) => emit!(self, n),
AbsoluteColorBase::NamedColorOrTransparent(n) => emit!(self, n),
AbsoluteColorBase::Function(n) => emit!(self, n),
}
}
#[emitter]
fn emit_hex_color(&mut self, n: &HexColor) -> Result {
let mut hex_color = String::with_capacity(9);
hex_color.push('#');
if self.config.minify {
let minified = minify_hex_color(&n.value);
hex_color.push_str(&minified);
} else {
hex_color.push_str(&n.value);
}
write_raw!(self, n.span, &hex_color);
}
#[emitter]
fn emit_alpha_value(&mut self, n: &AlphaValue) -> Result {
match n {
AlphaValue::Number(n) => emit!(self, n),
AlphaValue::Percentage(n) => emit!(self, n),
}
}
#[emitter]
fn emit_hue(&mut self, n: &Hue) -> Result {
match n {
Hue::Number(n) => emit!(self, n),
Hue::Angle(n) => emit!(self, n),
}
}
#[emitter]
fn emit_cmyk_component(&mut self, n: &CmykComponent) -> Result {
match n {
CmykComponent::Number(n) => emit!(self, n),
CmykComponent::Percentage(n) => emit!(self, n),
CmykComponent::Function(n) => emit!(self, n),
}
}
#[emitter]
fn emit_delimiter(&mut self, n: &Delimiter) -> Result {
write_raw!(self, n.span, n.value.as_str());
}
#[emitter]
fn emit_calc_sum(&mut self, n: &CalcSum) -> Result {
self.emit_list(&n.expressions, ListFormat::NotDelimited)?;
}
#[emitter]
fn emit_calc_product_or_operator(&mut self, n: &CalcProductOrOperator) -> Result {
match n {
CalcProductOrOperator::Product(n) => emit!(self, n),
CalcProductOrOperator::Operator(n) => emit!(self, n),
}
}
#[emitter]
fn emit_calc_operator(&mut self, n: &CalcOperator) -> Result {
let need_space = matches!(n.value, CalcOperatorType::Add | CalcOperatorType::Sub);
if need_space {
space!(self);
} else {
formatting_space!(self);
}
write_raw!(self, n.span, n.value.as_str());
if need_space {
space!(self);
} else {
formatting_space!(self);
}
}
#[emitter]
fn emit_calc_product(&mut self, n: &CalcProduct) -> Result {
self.emit_list(&n.expressions, ListFormat::None)?;
}
#[emitter]
fn emit_calc_value_or_operator(&mut self, n: &CalcValueOrOperator) -> Result {
match n {
CalcValueOrOperator::Value(n) => emit!(self, n),
CalcValueOrOperator::Operator(n) => emit!(self, n),
}
}
#[emitter]
fn emit_calc_value(&mut self, n: &CalcValue) -> Result {
match n {
CalcValue::Number(n) => emit!(self, n),
CalcValue::Dimension(n) => emit!(self, n),
CalcValue::Percentage(n) => emit!(self, n),
CalcValue::Constant(n) => emit!(self, n),
CalcValue::Sum(n) => {
write_raw!(self, lo_span_offset!(n.span, 1), "(");
emit!(self, n);
write_raw!(self, hi_span_offset!(n.span, 1), ")");
}
CalcValue::Function(n) => emit!(self, n),
}
}
#[emitter]
fn emit_token_and_span(&mut self, n: &TokenAndSpan) -> Result {
let span = n.span;
match &n.token {
Token::AtKeyword { raw, .. } => {
let mut at_keyword = String::with_capacity(1 + raw.len());
at_keyword.push('@');
at_keyword.push_str(raw);
write_raw!(self, span, &at_keyword);
}
Token::Delim { value } => {
write_raw!(self, span, &value.to_string());
}
Token::LParen => {
write_raw!(self, span, "(");
}
Token::RParen => {
write_raw!(self, span, ")");
}
Token::LBracket => {
write_raw!(self, span, "[");
}
Token::RBracket => {
write_raw!(self, span, "]");
}
Token::Number { raw, .. } => {
write_raw!(self, span, raw);
}
Token::Percentage { raw, .. } => {
let mut percentage = String::with_capacity(raw.len() + 1);
percentage.push_str(raw);
percentage.push('%');
write_raw!(self, span, &percentage);
}
Token::Dimension(token) => {
let mut dimension =
String::with_capacity(token.raw_value.len() + token.raw_unit.len());
dimension.push_str(&token.raw_value);
dimension.push_str(&token.raw_unit);
write_raw!(self, span, &dimension);
}
Token::Ident { raw, .. } => {
write_raw!(self, span, raw);
}
Token::Function { raw, .. } => {
let mut function = String::with_capacity(raw.len() + 1);
function.push_str(raw);
function.push('(');
write_raw!(self, span, &function);
}
Token::BadString { raw } => {
write_str!(self, span, raw);
}
Token::String { raw, .. } => {
write_str!(self, span, raw);
}
Token::Url { raw, .. } => {
let mut url = String::with_capacity(raw.0.len() + raw.1.len() + 2);
url.push_str(&raw.0);
url.push('(');
url.push_str(&raw.1);
url.push(')');
write_str!(self, span, &url);
}
Token::BadUrl { raw, .. } => {
write_str!(self, span, raw);
}
Token::Comma => {
write_raw!(self, span, ",");
}
Token::Semi => {
write_raw!(self, span, ";");
}
Token::LBrace => {
write_raw!(self, span, "{");
}
Token::RBrace => {
write_raw!(self, span, "}");
}
Token::Colon => {
write_raw!(self, span, ":");
}
Token::Hash { raw, .. } => {
let mut hash = String::with_capacity(raw.len() + 1);
hash.push('#');
hash.push_str(raw);
write_raw!(self, span, &hash);
}
Token::WhiteSpace { value } => {
write_str!(self, span, value);
}
Token::CDC => {
write_raw!(self, span, "-->");
}
Token::CDO => {
write_raw!(self, span, "<!--");
}
}
}
#[emitter]
fn emit_url(&mut self, n: &Url) -> Result {
emit!(
&mut *self.with_ctx(Ctx {
allow_to_lowercase: true,
..self.ctx
}),
n.name
);
write_raw!(self, "(");
if let Some(value) = &n.value {
emit!(self, value);
}
if let Some(modifiers) = &n.modifiers {
if !modifiers.is_empty() {
if n.value.is_some() {
formatting_space!(self);
}
self.emit_list(modifiers, ListFormat::SpaceDelimited)?;
}
}
write_raw!(self, ")");
}
#[emitter]
fn emit_url_value(&mut self, n: &UrlValue) -> Result {
match n {
UrlValue::Raw(n) => emit!(self, n),
UrlValue::Str(n) => emit!(self, n),
}
}
#[emitter]
fn emit_url_value_raw(&mut self, n: &UrlValueRaw) -> Result {
write_str!(self, n.span, &serialize_url(&n.value));
}
#[emitter]
fn emit_url_modifier(&mut self, n: &UrlModifier) -> Result {
match n {
UrlModifier::Ident(n) => emit!(self, n),
UrlModifier::Function(n) => emit!(self, n),
}
}
#[emitter]
fn emit_unicode_range(&mut self, n: &UnicodeRange) -> Result {
let mut value = String::with_capacity(
n.start.len()
+ if let Some(end) = &n.end {
end.len() + 1
} else {
0
}
+ 2,
);
value.push_str("u+");
value.push_str(&n.start);
if let Some(end) = &n.end {
value.push('-');
value.push_str(end);
}
write_raw!(self, n.span, &value);
}
#[emitter]
fn emit_family_name(&mut self, n: &FamilyName) -> Result {
match n {
FamilyName::Str(n) => emit!(self, n),
FamilyName::SequenceOfCustomIdents(n) => emit!(self, n),
}
}
#[emitter]
fn emit_sequence_of_custom_idents(&mut self, n: &SequenceOfCustomIdents) -> Result {
self.emit_list(&n.value, ListFormat::SpaceDelimited)?;
}
#[emitter]
fn emit_selector_list(&mut self, n: &SelectorList) -> Result {
self.emit_list(
&n.children,
if self.config.minify || self.ctx.in_single_line_selectors {
ListFormat::CommaDelimited
} else {
ListFormat::CommaDelimited | ListFormat::MultiLine
},
)?;
}
#[emitter]
fn emit_forgiving_selector_list(&mut self, n: &ForgivingSelectorList) -> Result {
for (idx, node) in n.children.iter().enumerate() {
if idx != 0 {
write_raw!(self, ",");
let need_space = matches!(node, ForgivingComplexSelector::ComplexSelector(_));
if need_space {
formatting_space!(self);
}
}
emit!(self, node)
}
}
#[emitter]
fn emit_forgiving_complex_list(&mut self, n: &ForgivingComplexSelector) -> Result {
match n {
ForgivingComplexSelector::ComplexSelector(n) => emit!(self, n),
ForgivingComplexSelector::ListOfComponentValues(n) => {
emit!(
&mut *self.with_ctx(Ctx {
in_list_of_component_values: true,
..self.ctx
}),
n
)
}
}
}
#[emitter]
fn emit_compound_selector_list(&mut self, n: &CompoundSelectorList) -> Result {
self.emit_list(&n.children, ListFormat::CommaDelimited)?;
}
#[emitter]
fn emit_relative_selector_list(&mut self, n: &RelativeSelectorList) -> Result {
self.emit_list(&n.children, ListFormat::CommaDelimited)?;
}
#[emitter]
fn emit_forgiving_relative_selector_list(
&mut self,
n: &ForgivingRelativeSelectorList,
) -> Result {
for (idx, node) in n.children.iter().enumerate() {
if idx != 0 {
write_raw!(self, ",");
let need_space = matches!(node, ForgivingRelativeSelector::RelativeSelector(_));
if need_space {
formatting_space!(self);
}
}
emit!(self, node)
}
}
#[emitter]
fn emit_forgiving_relative_selector(&mut self, n: &ForgivingRelativeSelector) -> Result {
match n {
ForgivingRelativeSelector::RelativeSelector(n) => emit!(self, n),
ForgivingRelativeSelector::ListOfComponentValues(n) => {
emit!(
&mut *self.with_ctx(Ctx {
in_list_of_component_values: true,
..self.ctx
}),
n
)
}
}
}
#[emitter]
fn emit_complex_selector(&mut self, n: &ComplexSelector) -> Result {
for (idx, node) in n.children.iter().enumerate() {
emit!(self, node);
match node {
ComplexSelectorChildren::Combinator(Combinator {
value: CombinatorValue::Descendant,
..
}) => {}
_ => match n.children.get(idx + 1) {
Some(ComplexSelectorChildren::Combinator(Combinator {
value: CombinatorValue::Descendant,
..
})) => {}
Some(_) => {
formatting_space!(self);
}
_ => {}
},
}
}
}
#[emitter]
fn emit_relative_selector(&mut self, n: &RelativeSelector) -> Result {
if let Some(combinator) = &n.combinator {
emit!(self, combinator);
formatting_space!(self);
}
emit!(self, n.selector);
}
#[emitter]
fn emit_complex_selector_children(&mut self, n: &ComplexSelectorChildren) -> Result {
match n {
ComplexSelectorChildren::CompoundSelector(n) => emit!(self, n),
ComplexSelectorChildren::Combinator(n) => emit!(self, n),
}
}
#[emitter]
fn emit_compound_selector(&mut self, n: &CompoundSelector) -> Result {
emit!(self, n.nesting_selector);
emit!(self, n.type_selector);
self.emit_list(&n.subclass_selectors, ListFormat::NotDelimited)?;
}
#[emitter]
fn emit_combinator(&mut self, n: &Combinator) -> Result {
write_raw!(self, n.span, n.value.as_str());
}
#[emitter]
fn emit_nesting_selector(&mut self, n: &NestingSelector) -> Result {
write_raw!(self, n.span, "&");
}
#[emitter]
fn emit_subclass_selector(&mut self, n: &SubclassSelector) -> Result {
match n {
SubclassSelector::Id(n) => emit!(self, n),
SubclassSelector::Class(n) => emit!(self, n),
SubclassSelector::Attribute(n) => emit!(self, n),
SubclassSelector::PseudoClass(n) => emit!(self, n),
SubclassSelector::PseudoElement(n) => emit!(self, n),
}
}
#[emitter]
fn emit_type_selector(&mut self, n: &TypeSelector) -> Result {
match n {
TypeSelector::TagName(n) => emit!(self, n),
TypeSelector::Universal(n) => emit!(self, n),
}
}
#[emitter]
fn emit_tag_name_selector(&mut self, n: &TagNameSelector) -> Result {
emit!(
&mut *self.with_ctx(Ctx {
allow_to_lowercase: true,
..self.ctx
}),
n.name
);
}
#[emitter]
fn emit_universal_selector(&mut self, n: &UniversalSelector) -> Result {
if let Some(prefix) = &n.prefix {
emit!(self, prefix);
}
write_raw!(self, hi_span_offset!(n.span, 1), "*");
}
#[emitter]
fn emit_namespace_prefix(&mut self, n: &NamespacePrefix) -> Result {
if let Some(namespace) = &n.namespace {
emit!(self, namespace);
}
write_raw!(self, hi_span_offset!(n.span, 1), "|");
}
#[emitter]
fn emit_namespace(&mut self, n: &Namespace) -> Result {
match n {
Namespace::Named(n) => emit!(self, n),
Namespace::Any(n) => emit!(self, n),
}
}
#[emitter]
fn emit_named_namespace(&mut self, n: &NamedNamespace) -> Result {
emit!(self, n.name);
}
#[emitter]
fn emit_any_namespace(&mut self, n: &AnyNamespace) -> Result {
write_raw!(self, n.span, "*");
}
#[emitter]
fn emit_wq_name(&mut self, n: &WqName) -> Result {
if n.prefix.is_some() {
emit!(self, n.prefix);
}
emit!(self, n.value);
}
#[emitter]
fn emit_id_selector(&mut self, n: &IdSelector) -> Result {
write_raw!(self, lo_span_offset!(n.span, 1), "#");
emit!(self, n.text);
}
#[emitter]
fn emit_class_selector(&mut self, n: &ClassSelector) -> Result {
write_raw!(self, lo_span_offset!(n.span, 1), ".");
emit!(self, n.text);
}
#[emitter]
fn emit_attribute_selector(&mut self, n: &AttributeSelector) -> Result {
write_raw!(self, lo_span_offset!(n.span, 1), "[");
emit!(self, n.name);
if n.matcher.is_some() {
emit!(self, n.matcher);
emit!(self, n.value);
if n.modifier.is_some() {
match n.value {
Some(AttributeSelectorValue::Str(_)) => {
formatting_space!(self);
}
Some(AttributeSelectorValue::Ident(_)) => {
space!(self);
}
_ => {}
}
emit!(self, n.modifier);
}
}
write_raw!(self, hi_span_offset!(n.span, 1), "]");
}
#[emitter]
fn emit_attribute_selector_matcher(&mut self, n: &AttributeSelectorMatcher) -> Result {
write_raw!(self, n.span, n.value.as_str());
}
#[emitter]
fn emit_attribute_selector_value(&mut self, n: &AttributeSelectorValue) -> Result {
match n {
AttributeSelectorValue::Str(n) => emit!(self, n),
AttributeSelectorValue::Ident(n) => emit!(self, n),
}
}
#[emitter]
fn emit_attribute_selector_modifier(&mut self, n: &AttributeSelectorModifier) -> Result {
emit!(
&mut *self.with_ctx(Ctx {
allow_to_lowercase: true,
..self.ctx
}),
n.value
);
}
#[emitter]
fn emit_an_plus_b(&mut self, n: &AnPlusB) -> Result {
match n {
AnPlusB::Ident(n) => emit!(self, n),
AnPlusB::AnPlusBNotation(n) => emit!(self, n),
}
}
#[emitter]
fn emit_an_plus_b_notation(&mut self, n: &AnPlusBNotation) -> Result {
let mut an_plus_b = String::with_capacity(4);
if let Some(a) = &n.a {
if *a == -1 {
an_plus_b.push('-');
} else if *a != 1 {
an_plus_b.push_str(&a.to_string());
}
an_plus_b.push('n');
}
if let Some(b) = &n.b {
if *b >= 0 && n.a.is_some() {
an_plus_b.push('+');
}
an_plus_b.push_str(&b.to_string());
}
write_raw!(self, n.span, &an_plus_b);
}
#[emitter]
fn emit_pseudo_class_selector(&mut self, n: &PseudoClassSelector) -> Result {
write_raw!(self, lo_span_offset!(n.span, 1), ":");
emit!(
&mut *self.with_ctx(Ctx {
allow_to_lowercase: true,
..self.ctx
}),
n.name
);
if let Some(children) = &n.children {
write_raw!(self, "(");
self.emit_list_pseudo_class_selector_children(children)?;
write_raw!(self, ")");
}
}
#[emitter]
fn emit_pseudo_class_selector_children(&mut self, n: &PseudoClassSelectorChildren) -> Result {
match n {
PseudoClassSelectorChildren::PreservedToken(n) => emit!(self, n),
PseudoClassSelectorChildren::AnPlusB(n) => emit!(self, n),
PseudoClassSelectorChildren::Ident(n) => emit!(self, n),
PseudoClassSelectorChildren::Str(n) => emit!(self, n),
PseudoClassSelectorChildren::Delimiter(n) => emit!(self, n),
PseudoClassSelectorChildren::ComplexSelector(n) => emit!(self, n),
PseudoClassSelectorChildren::SelectorList(n) => emit!(
&mut *self.with_ctx(Ctx {
in_single_line_selectors: true,
..self.ctx
}),
n
),
PseudoClassSelectorChildren::ForgivingSelectorList(n) => emit!(
&mut *self.with_ctx(Ctx {
in_single_line_selectors: true,
..self.ctx
}),
n
),
PseudoClassSelectorChildren::CompoundSelectorList(n) => emit!(self, n),
PseudoClassSelectorChildren::RelativeSelectorList(n) => emit!(self, n),
PseudoClassSelectorChildren::ForgivingRelativeSelectorList(n) => emit!(self, n),
PseudoClassSelectorChildren::CompoundSelector(n) => emit!(self, n),
}
}
fn emit_list_pseudo_class_selector_children(
&mut self,
nodes: &[PseudoClassSelectorChildren],
) -> Result {
let len = nodes.len();
for (idx, node) in nodes.iter().enumerate() {
emit!(self, node);
if idx != len - 1 {
match node {
PseudoClassSelectorChildren::PreservedToken(_) => {}
PseudoClassSelectorChildren::Delimiter(_) => {
formatting_space!(self);
}
_ => {
let next = nodes.get(idx + 1);
match next {
Some(PseudoClassSelectorChildren::Delimiter(Delimiter {
value: DelimiterValue::Comma,
..
})) => {}
_ => {
space!(self)
}
}
}
}
}
}
Ok(())
}
#[emitter]
fn emit_pseudo_element_selector(&mut self, n: &PseudoElementSelector) -> Result {
write_raw!(self, lo_span_offset!(n.span, 1), ":");
write_raw!(self, lo_span_offset!(n.span, 2), ":");
emit!(
&mut *self.with_ctx(Ctx {
allow_to_lowercase: true,
..self.ctx
}),
n.name
);
if let Some(children) = &n.children {
write_raw!(self, "(");
self.emit_list_pseudo_element_selector_children(children)?;
write_raw!(self, ")");
}
}
#[emitter]
fn emit_pseudo_element_selector_children(
&mut self,
n: &PseudoElementSelectorChildren,
) -> Result {
match n {
PseudoElementSelectorChildren::PreservedToken(n) => emit!(self, n),
PseudoElementSelectorChildren::Ident(n) => emit!(self, n),
PseudoElementSelectorChildren::CompoundSelector(n) => emit!(self, n),
PseudoElementSelectorChildren::CustomHighlightName(n) => emit!(self, n),
}
}
#[emitter]
fn emit_scope_range(&mut self, n: &ScopeRange) -> Result {
if let Some(start) = &n.scope_start {
formatting_space!(self);
write_raw!(self, "(");
emit!(self, start);
write_raw!(self, ")");
}
if let Some(end) = &n.scope_end {
write_raw!(self, " to");
space!(self);
write_raw!(self, "(");
emit!(self, end);
write_raw!(self, ")");
}
}
fn emit_list_pseudo_element_selector_children(
&mut self,
nodes: &[PseudoElementSelectorChildren],
) -> Result {
let len = nodes.len();
for (idx, node) in nodes.iter().enumerate() {
emit!(self, node);
if idx != len - 1 {
match node {
PseudoElementSelectorChildren::PreservedToken(_) => {}
_ => {
space!(self)
}
}
}
}
Ok(())
}
fn emit_list<N>(&mut self, nodes: &[N], format: ListFormat) -> Result
where
Self: Emit<N>,
N: Spanned,
{
for (idx, node) in nodes.iter().enumerate() {
if idx != 0 {
self.write_delim(format)?;
if format & ListFormat::LinesMask == ListFormat::MultiLine {
formatting_newline!(self);
}
}
emit!(self, node)
}
Ok(())
}
fn write_delim(&mut self, f: ListFormat) -> Result {
match f & ListFormat::DelimitersMask {
ListFormat::None => {}
ListFormat::CommaDelimited => {
write_raw!(self, ",");
formatting_space!(self);
}
ListFormat::SpaceDelimited => {
space!(self)
}
ListFormat::SemiDelimited => {
write_raw!(self, ";")
}
ListFormat::DotDelimited => {
write_raw!(self, ".");
}
_ => unreachable!(),
}
Ok(())
}
}
fn minify_numeric(value: f64) -> String {
if value.is_sign_negative() && value == 0.0 {
return "-0".to_owned();
}
let mut minified = value.to_string();
if minified.starts_with("0.") {
minified.replace_range(0..1, "");
} else if minified.starts_with("-0.") {
minified.replace_range(1..2, "");
}
if minified.starts_with(".000") {
let mut cnt = 3;
for &v in minified.as_bytes().iter().skip(4) {
if v == b'0' {
cnt += 1;
} else {
break;
}
}
minified.replace_range(0..cnt + 1, "");
let remain_len = minified.len();
minified.push_str("e-");
minified.push_str(&(remain_len + cnt).to_string());
} else if minified.ends_with("000") {
let mut cnt = 3;
for &v in minified.as_bytes().iter().rev().skip(3) {
if v == b'0' {
cnt += 1;
} else {
break;
}
}
minified.truncate(minified.len() - cnt);
minified.push('e');
minified.push_str(&cnt.to_string());
}
minified
}
fn minify_hex_color(value: &str) -> String {
let length = value.len();
if length == 6 || length == 8 {
let chars = value.as_bytes();
if chars[0] == chars[1] && chars[2] == chars[3] && chars[4] == chars[5] {
if length == 6 || chars[6] == b'f' && chars[7] == b'f' {
let mut minified = String::with_capacity(3);
minified.push(chars[0] as char);
minified.push(chars[2] as char);
minified.push(chars[4] as char);
return minified;
}
else if length == 8 && chars[6] == chars[7] {
let mut minified = String::with_capacity(4);
minified.push(chars[0] as char);
minified.push(chars[2] as char);
minified.push(chars[4] as char);
minified.push(chars[6] as char);
return minified;
}
}
}
value.to_string()
}
fn serialize_string(value: &str) -> String {
let mut minified = String::with_capacity(value.len());
for c in value.chars() {
match c {
'\0' => {
minified.push('\u{FFFD}');
}
'\x01'..='\x1F' | '\x7F' => {
static HEX_DIGITS: &[u8; 16] = b"0123456789abcdef";
let b3;
let b4;
let char_as_u8 = c as u8;
let bytes = if char_as_u8 > 0x0f {
let high = (char_as_u8 >> 4) as usize;
let low = (char_as_u8 & 0x0f) as usize;
b4 = [b'\\', HEX_DIGITS[high], HEX_DIGITS[low], b' '];
&b4[..]
} else {
b3 = [b'\\', HEX_DIGITS[c as usize], b' '];
&b3[..]
};
minified.push_str(from_utf8(bytes).unwrap());
}
'\\' => {
minified.push_str("\\\\");
}
'"' => {
minified.push('\"');
}
_ => {
minified.push(c);
}
};
}
format!("\"{}\"", minified.replace('"', "\\\""))
}
fn serialize_url(value: &str) -> String {
let mut new_value = String::with_capacity(value.len());
for c in value.chars() {
match c {
'\x01'..='\x1F' | '\x7F' => {
static HEX_DIGITS: &[u8; 16] = b"0123456789abcdef";
let b3;
let b4;
let char_as_u8 = c as u8;
let bytes = if char_as_u8 > 0x0f {
let high = (char_as_u8 >> 4) as usize;
let low = (char_as_u8 & 0x0f) as usize;
b4 = [b'\\', HEX_DIGITS[high], HEX_DIGITS[low], b' '];
&b4[..]
} else {
b3 = [b'\\', HEX_DIGITS[c as usize], b' '];
&b3[..]
};
new_value.push_str(from_utf8(bytes).unwrap());
}
'(' | ')' | '"' | '\'' => {
new_value.push('\\');
new_value.push(c)
}
'\\' => {
new_value.push_str("\\\\");
}
_ if c.is_whitespace() => {
new_value.push('\\');
new_value.push(c)
}
_ => {
new_value.push(c);
}
};
}
new_value
}
fn minify_string(value: &str) -> String {
let mut minified = String::with_capacity(value.len());
let mut dq = 0;
let mut sq = 0;
for c in value.chars() {
match c {
'\0' => {
minified.push('\u{FFFD}');
}
'\x01'..='\x1F' | '\x7F' => {
static HEX_DIGITS: &[u8; 16] = b"0123456789abcdef";
let b3;
let b4;
let char_as_u8 = c as u8;
let bytes = if char_as_u8 > 0x0f {
let high = (char_as_u8 >> 4) as usize;
let low = (char_as_u8 & 0x0f) as usize;
b4 = [b'\\', HEX_DIGITS[high], HEX_DIGITS[low], b' '];
&b4[..]
} else {
b3 = [b'\\', HEX_DIGITS[c as usize], b' '];
&b3[..]
};
minified.push_str(from_utf8(bytes).unwrap());
}
'\\' => {
minified.push_str("\\\\");
}
'"' => {
dq += 1;
minified.push(c);
}
'\'' => {
sq += 1;
minified.push(c);
}
_ => {
minified.push(c);
}
};
}
if dq > sq {
format!("'{}'", minified.replace('\'', "\\'"))
} else {
format!("\"{}\"", minified.replace('"', "\\\""))
}
}
fn serialize_dimension_unit(value: &str) -> Cow<'_, str> {
let need_escape =
(value.len() >= 2 && value.as_bytes()[0] == b'e' && value.as_bytes()[1].is_ascii_digit())
|| value.contains(char::REPLACEMENT_CHARACTER);
if !need_escape {
return Cow::Borrowed(value);
}
let mut result = String::with_capacity(value.len());
let mut chars = value.chars().enumerate().peekable();
while let Some((i, c)) = chars.next() {
match c {
char::REPLACEMENT_CHARACTER => {
result.push_str("\\0");
}
'e' if i == 0 => {
if matches!(chars.peek(), Some((_, '0'..='9'))) {
result.push(c);
result.push_str("\\3");
} else {
result.push(c);
}
}
_ => {
result.push(c);
}
}
}
Cow::Owned(result)
}