From ea3aa002bb9ab17500aa07d2245fb0c05c8ff870 Mon Sep 17 00:00:00 2001 From: AsyncIter Date: Thu, 25 Jun 2026 19:14:00 +0800 Subject: [PATCH 1/9] feat: support custom source map implementation --- Cargo.toml | 3 +- benches/common/mod.rs | 38 +- benches/transform.rs | 9 +- c/src/lib.rs | 7 +- derive/src/to_css.rs | 10 +- examples/custom_at_rule.rs | 47 +-- napi/src/at_rule_parser.rs | 21 +- napi/src/lib.rs | 73 ++-- src/bundler.rs | 66 ++-- src/declaration.rs | 32 +- src/lib.rs | 297 ++++++++++----- src/macros.rs | 17 +- src/main.rs | 16 +- src/media_query.rs | 120 +++--- src/parser.rs | 3 +- src/printer.rs | 629 ++++++++++++++++++++++--------- src/properties/align.rs | 125 +++--- src/properties/animation.rs | 79 ++-- src/properties/background.rs | 43 ++- src/properties/border.rs | 6 +- src/properties/border_image.rs | 25 +- src/properties/border_radius.rs | 6 +- src/properties/box_shadow.rs | 6 +- src/properties/contain.rs | 16 +- src/properties/css_modules.rs | 11 +- src/properties/custom.rs | 101 ++--- src/properties/display.rs | 69 ++-- src/properties/effects.rs | 51 +-- src/properties/flex.rs | 11 +- src/properties/font.rs | 55 ++- src/properties/grid.rs | 168 ++++----- src/properties/list.rs | 19 +- src/properties/margin_padding.rs | 1 - src/properties/masking.rs | 21 +- src/properties/mod.rs | 16 +- src/properties/outline.rs | 1 - src/properties/overflow.rs | 6 +- src/properties/position.rs | 29 +- src/properties/size.rs | 90 +++-- src/properties/svg.rs | 11 +- src/properties/text.rs | 57 +-- src/properties/transform.rs | 143 +++---- src/properties/transition.rs | 6 +- src/properties/ui.rs | 28 +- src/rules/container.rs | 68 ++-- src/rules/counter_style.rs | 7 +- src/rules/custom_media.rs | 10 +- src/rules/document.rs | 10 +- src/rules/font_face.rs | 56 +-- src/rules/font_feature_values.rs | 22 +- src/rules/font_palette_values.rs | 37 +- src/rules/import.rs | 15 +- src/rules/keyframes.rs | 51 ++- src/rules/layer.rs | 24 +- src/rules/media.rs | 13 +- src/rules/mod.rs | 35 +- src/rules/namespace.rs | 10 +- src/rules/nesting.rs | 27 +- src/rules/page.rs | 29 +- src/rules/position_try.rs | 7 +- src/rules/property.rs | 12 +- src/rules/scope.rs | 31 +- src/rules/starting_style.rs | 10 +- src/rules/style.rs | 49 +-- src/rules/supports.rs | 40 +- src/rules/unknown.rs | 13 +- src/rules/view_transition.rs | 17 +- src/rules/viewport.rs | 7 +- src/selector.rs | 519 ++++++++++++++++--------- src/stylesheet.rs | 116 +++--- src/traits.rs | 21 +- src/values/alpha.rs | 6 +- src/values/angle.rs | 14 +- src/values/calc.rs | 51 +-- src/values/color.rs | 57 ++- src/values/easing.rs | 62 ++- src/values/gradient.rs | 131 +++---- src/values/ident.rs | 46 +-- src/values/image.rs | 30 +- src/values/length.rs | 49 ++- src/values/number.rs | 17 +- src/values/percentage.rs | 17 +- src/values/position.rs | 22 +- src/values/ratio.rs | 6 +- src/values/rect.rs | 6 +- src/values/resolution.rs | 8 +- src/values/shape.rs | 43 +-- src/values/size.rs | 6 +- src/values/string.rs | 8 +- src/values/syntax.rs | 38 +- src/values/time.rs | 18 +- src/values/url.rs | 14 +- src/vendor_prefix.rs | 26 +- src/visitor.rs | 4 +- tests/test_custom_parser.rs | 28 +- 95 files changed, 2485 insertions(+), 2066 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 31fdac0e9..a94ca6ba9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,7 +49,8 @@ serde = [ "parcel_selectors/serde", "into_owned", ] -sourcemap = ["parcel_sourcemap"] +sourcemap = ["custom_sourcemap", "dep:parcel_sourcemap"] +custom_sourcemap = [] visitor = [] into_owned = [ "static-self", diff --git a/benches/common/mod.rs b/benches/common/mod.rs index 5c2a96948..51298a912 100644 --- a/benches/common/mod.rs +++ b/benches/common/mod.rs @@ -1,5 +1,6 @@ use criterion::black_box; use rspack_lightningcss::stylesheet::{MinifyOptions, ParserOptions, PrinterOptions, StyleSheet}; +use parcel_sourcemap::SourceMap; use std::fmt::Write; pub struct Fixture { @@ -37,14 +38,43 @@ pub fn transform_stylesheet(css: &str) { let mut stylesheet = StyleSheet::parse(css, ParserOptions::default()).unwrap(); stylesheet.minify(MinifyOptions::default()).unwrap(); let result = stylesheet - .to_css(PrinterOptions { - minify: true, - ..PrinterOptions::default() - }) + .to_css( + PrinterOptions { + minify: true, + ..PrinterOptions::default() + }, + None::<&mut ()>, + ) .unwrap(); black_box(result); } +pub fn transform_stylesheet_with_source_map(css: &str) { + let mut source_map = SourceMap::new("/"); + let source_index = source_map.add_source("bench.css"); + source_map.set_source_content(source_index as usize, css).unwrap(); + + let mut stylesheet = StyleSheet::parse( + css, + ParserOptions { + source_index, + ..ParserOptions::default() + }, + ) + .unwrap(); + stylesheet.minify(MinifyOptions::default()).unwrap(); + let result = stylesheet + .to_css( + PrinterOptions { + minify: true, + ..PrinterOptions::default() + }, + Some(&mut source_map), + ) + .unwrap(); + black_box((result, source_map)); +} + fn small_fixture() -> String { r#" :root { diff --git a/benches/transform.rs b/benches/transform.rs index 2c7d66ce8..abee3595a 100644 --- a/benches/transform.rs +++ b/benches/transform.rs @@ -27,12 +27,19 @@ fn bench_stylesheet_transform(c: &mut Criterion) { for fixture in &fixtures { group.throughput(Throughput::Bytes(fixture.css.len() as u64)); group.bench_with_input( - BenchmarkId::from_parameter(fixture.name), + BenchmarkId::new("no-source-map", fixture.name), fixture.css.as_str(), |b, css| { b.iter(|| common::transform_stylesheet(black_box(css))); }, ); + group.bench_with_input( + BenchmarkId::new("source-map", fixture.name), + fixture.css.as_str(), + |b, css| { + b.iter(|| common::transform_stylesheet_with_source_map(black_box(css))); + }, + ); } group.finish(); diff --git a/c/src/lib.rs b/c/src/lib.rs index 78b8c763f..5f1279fd9 100644 --- a/c/src/lib.rs +++ b/c/src/lib.rs @@ -338,7 +338,6 @@ pub extern "C" fn lightningcss_stylesheet_to_css( } else { Some(unsafe { std::str::from_utf8_unchecked(CStr::from_ptr(options.project_root).to_bytes()) }) }, - source_map: source_map.as_mut(), targets: if options.targets != Targets::default() { Some(options.targets.into()).into() } else { @@ -356,7 +355,11 @@ pub extern "C" fn lightningcss_stylesheet_to_css( }, }; - let res = unwrap!(wrapper.stylesheet.to_css(opts), error, ToCssResult::default()); + let res = unwrap!( + wrapper.stylesheet.to_css(opts, source_map.as_mut()), + error, + ToCssResult::default() + ); let map = if let Some(mut source_map) = source_map { if !options.input_source_map.is_null() { diff --git a/derive/src/to_css.rs b/derive/src/to_css.rs index 739a16d40..c9665a45c 100644 --- a/derive/src/to_css.rs +++ b/derive/src/to_css.rs @@ -25,10 +25,7 @@ pub fn derive_to_css(input: TokenStream) -> TokenStream { let output = quote! { impl #impl_generics ToCss for #ident #ty_generics #where_clause { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { #imp } } @@ -121,7 +118,10 @@ fn derive_enum(data: &DataEnum, opts: &CssOptions) -> TokenStream2 { Fields::Unit => { let s = Literal::string(&variant.ident.to_string().to_case(opts.case)); quote! { - Self::#name => dest.write_str(#s) + Self::#name => { + dest.write_str(#s)?; + Ok(()) + } } } Fields::Named(_) => { diff --git a/examples/custom_at_rule.rs b/examples/custom_at_rule.rs index 324e9a82f..1a453e781 100644 --- a/examples/custom_at_rule.rs +++ b/examples/custom_at_rule.rs @@ -2,22 +2,22 @@ use std::{collections::HashMap, convert::Infallible}; use cssparser::*; use rspack_lightningcss::{ - declaration::DeclarationBlock, - error::PrinterError, - printer::Printer, - properties::custom::{Token, TokenOrValue}, - rules::{style::StyleRule, CssRule, CssRuleList, Location}, - selector::{Component, Selector}, - stylesheet::{ParserOptions, PrinterOptions, StyleSheet}, - targets::Browsers, - traits::{AtRuleParser, ToCss}, - values::{ + declaration::DeclarationBlock, + error::PrinterError, + printer::PrinterTrait, + properties::custom::{Token, TokenOrValue}, + rules::{style::StyleRule, CssRule, CssRuleList, Location}, + selector::{Component, Selector}, + stylesheet::{ParserOptions, PrinterOptions, StyleSheet}, + targets::Browsers, + traits::{AtRuleParser, ToCss}, + values::{ color::{CssColor, RGBA}, length::LengthValue, }, - vendor_prefix::VendorPrefix, - visit_types, - visitor::{Visit, VisitTypes, Visitor}, + vendor_prefix::VendorPrefix, + visit_types, + visitor::{Visit, VisitTypes, Visitor}, }; fn main() { @@ -42,14 +42,17 @@ fn main() { stylesheet.visit(&mut ApplyVisitor { rules: &style_rules }).unwrap(); let result = stylesheet - .to_css(PrinterOptions { - targets: Browsers { - chrome: Some(100 << 16), - ..Browsers::default() - } - .into(), - ..PrinterOptions::default() - }) + .to_css( + PrinterOptions { + targets: Browsers { + chrome: Some(100 << 16), + ..Browsers::default() + } + .into(), + ..PrinterOptions::default() + }, + None::<&mut ()>, + ) .unwrap(); println!("{}", result.code); } @@ -294,7 +297,7 @@ impl<'i, V: Visitor<'i, AtRule>> Visit<'i, AtRule, V> for AtRule { } impl ToCss for AtRule { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { AtRule::Tailwind(rule) => { let _ = rule.loc; // TODO: source maps diff --git a/napi/src/at_rule_parser.rs b/napi/src/at_rule_parser.rs index 4915a8c18..a1d7ab58c 100644 --- a/napi/src/at_rule_parser.rs +++ b/napi/src/at_rule_parser.rs @@ -2,12 +2,13 @@ use std::collections::HashMap; use cssparser::*; use lightningcss::{ - declaration::DeclarationBlock, - error::ParserError, - rules::{CssRuleList, Location}, - stylesheet::ParserOptions, - traits::{AtRuleParser, ToCss}, - values::{ + declaration::DeclarationBlock, + error::ParserError, + printer::PrinterTrait, + rules::{CssRuleList, Location}, + stylesheet::ParserOptions, + traits::{AtRuleParser, ToCss}, + values::{ string::CowArcStr, syntax::{ParsedComponent, SyntaxString}, }, @@ -161,13 +162,7 @@ impl<'i> AtRuleParser<'i> for CustomAtRuleParser { } impl<'i> ToCss for AtRule<'i> { - fn to_css( - &self, - dest: &mut lightningcss::printer::Printer, - ) -> Result<(), lightningcss::error::PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), lightningcss::error::PrinterError> { dest.write_char('@')?; serialize_identifier(&self.name, dest)?; if let Some(prelude) = &self.prelude { diff --git a/napi/src/lib.rs b/napi/src/lib.rs index 7aa3932b0..0692f7502 100644 --- a/napi/src/lib.rs +++ b/napi/src/lib.rs @@ -763,24 +763,26 @@ fn compile<'i>( unused_symbols: config.unused_symbols.clone().unwrap_or_default(), })?; - stylesheet.to_css(PrinterOptions { - minify: config.minify.unwrap_or_default(), - source_map: source_map.as_mut(), - project_root, - targets, - analyze_dependencies: if let Some(d) = &config.analyze_dependencies { - match d { - AnalyzeDependenciesOption::Bool(b) if *b => Some(DependencyOptions { remove_imports: true }), - AnalyzeDependenciesOption::Config(c) => Some(DependencyOptions { - remove_imports: !c.preserve_imports, - }), - _ => None, - } - } else { - None + stylesheet.to_css( + PrinterOptions { + minify: config.minify.unwrap_or_default(), + project_root, + targets, + analyze_dependencies: if let Some(d) = &config.analyze_dependencies { + match d { + AnalyzeDependenciesOption::Bool(b) if *b => Some(DependencyOptions { remove_imports: true }), + AnalyzeDependenciesOption::Config(c) => Some(DependencyOptions { + remove_imports: !c.preserve_imports, + }), + _ => None, + } + } else { + None + }, + pseudo_classes: config.pseudo_classes.as_ref().map(|p| p.into()), }, - pseudo_classes: config.pseudo_classes.as_ref().map(|p| p.into()), - })? + source_map.as_mut(), + )? }; let map = if let Some(mut source_map) = source_map { @@ -898,24 +900,26 @@ fn compile_bundle<'i, 'o, P: SourceProvider, F: FnOnce(&mut StyleSheet<'i, AtRul unused_symbols: config.unused_symbols.clone().unwrap_or_default(), })?; - stylesheet.to_css(PrinterOptions { - minify: config.minify.unwrap_or_default(), - source_map: source_map.as_mut(), - project_root, - targets, - analyze_dependencies: if let Some(d) = &config.analyze_dependencies { - match d { - AnalyzeDependenciesOption::Bool(b) if *b => Some(DependencyOptions { remove_imports: true }), - AnalyzeDependenciesOption::Config(c) => Some(DependencyOptions { - remove_imports: !c.preserve_imports, - }), - _ => None, - } - } else { - None + stylesheet.to_css( + PrinterOptions { + minify: config.minify.unwrap_or_default(), + project_root, + targets, + analyze_dependencies: if let Some(d) = &config.analyze_dependencies { + match d { + AnalyzeDependenciesOption::Bool(b) if *b => Some(DependencyOptions { remove_imports: true }), + AnalyzeDependenciesOption::Config(c) => Some(DependencyOptions { + remove_imports: !c.preserve_imports, + }), + _ => None, + } + } else { + None + }, + pseudo_classes: config.pseudo_classes.as_ref().map(|p| p.into()), }, - pseudo_classes: config.pseudo_classes.as_ref().map(|p| p.into()), - })? + source_map.as_mut(), + )? }; let map = if let Some(source_map) = &mut source_map { @@ -1022,7 +1026,6 @@ fn compile_attr<'i>( }); attr.to_css(PrinterOptions { minify: config.minify, - source_map: None, project_root: None, targets, analyze_dependencies: if config.analyze_dependencies { diff --git a/src/bundler.rs b/src/bundler.rs index db852b270..717bf6109 100644 --- a/src/bundler.rs +++ b/src/bundler.rs @@ -19,7 +19,7 @@ //! }; //! //! let fs = FileProvider::new(); -//! let mut bundler = Bundler::new(&fs, None, ParserOptions::default()); +//! let mut bundler = Bundler::new(&fs, None::<&mut ()>, ParserOptions::default()); //! let stylesheet = bundler.bundle(Path::new("style.css")).unwrap(); //! ``` @@ -43,6 +43,7 @@ use crate::{ use crate::{ error::{Error, ParserError}, media_query::MediaList, + printer::SourceMap, rules::{ import::ImportRule, media::MediaRule, @@ -52,7 +53,6 @@ use crate::{ stylesheet::{ParserOptions, StyleSheet}, }; use dashmap::DashMap; -use parcel_sourcemap::SourceMap; use rayon::prelude::*; use std::{ collections::HashSet, @@ -63,8 +63,8 @@ use std::{ /// A Bundler combines a CSS file and all imported dependencies together into /// a single merged style sheet. -pub struct Bundler<'a, 's, P, T: AtRuleParser<'a>> { - source_map: Option>, +pub struct Bundler<'a, 's, P, T: AtRuleParser<'a>, S = ()> { + source_map: Option>, fs: &'a P, source_indexes: DashMap, stylesheets: Mutex>>, @@ -235,15 +235,15 @@ impl<'i, T: std::error::Error> BundleErrorKind<'i, T> { } } -impl<'a, 's, P: SourceProvider> Bundler<'a, 's, P, DefaultAtRuleParser> { +impl<'a, 's, P: SourceProvider, S: SourceMap + Send> Bundler<'a, 's, P, DefaultAtRuleParser, S> { /// Creates a new Bundler using the given source provider. /// If a source map is given, the content of each source file included in the bundle will /// be added accordingly. pub fn new( fs: &'a P, - source_map: Option<&'s mut SourceMap>, + source_map: Option<&'s mut S>, options: ParserOptions<'a>, - ) -> Bundler<'a, 's, P, DefaultAtRuleParser> { + ) -> Bundler<'a, 's, P, DefaultAtRuleParser, S> { Bundler { source_map: source_map.map(Mutex::new), fs, @@ -255,7 +255,8 @@ impl<'a, 's, P: SourceProvider> Bundler<'a, 's, P, DefaultAtRuleParser> { } } -impl<'a, 's, P: SourceProvider, T: AtRuleParser<'a> + Clone + Sync + Send> Bundler<'a, 's, P, T> +impl<'a, 's, P: SourceProvider, T: AtRuleParser<'a> + Clone + Sync + Send, S: SourceMap + Send> + Bundler<'a, 's, P, T, S> where T::AtRule: Sync + Send + ToCss + Clone, { @@ -264,7 +265,7 @@ where /// be added accordingly. pub fn new_with_at_rule_parser( fs: &'a P, - source_map: Option<&'s mut SourceMap>, + source_map: Option<&'s mut S>, options: ParserOptions<'a>, at_rule_parser: &'s mut T, ) -> Self { @@ -462,7 +463,7 @@ where if sm.is_none() || !sm.unwrap().starts_with("data") { let mut source_map = source_map.lock().unwrap(); let source_index = source_map.add_source(filename); - let _ = source_map.set_source_content(source_index as usize, code); + source_map.set_source_content(source_index, code); } } @@ -955,9 +956,9 @@ mod tests { ); fn bundle(fs: P, entry: &str) -> String { - let mut bundler = Bundler::new(&fs, None, ParserOptions::default()); + let mut bundler = Bundler::<_, _, ()>::new(&fs, None, ParserOptions::default()); let stylesheet = bundler.bundle(Path::new(entry)).unwrap(); - stylesheet.to_css(PrinterOptions::default()).unwrap().code + stylesheet.to_css(PrinterOptions::default(), None::<&mut ()>).unwrap().code } fn bundle_css_module( @@ -974,7 +975,7 @@ mod tests { project_root: Option<&str>, pattern: &'static str, ) -> (String, CssModuleExports) { - let mut bundler = Bundler::new( + let mut bundler = Bundler::<_, _, ()>::new( &fs, None, ParserOptions { @@ -989,16 +990,19 @@ mod tests { let mut stylesheet = bundler.bundle(Path::new(entry)).unwrap(); stylesheet.minify(MinifyOptions::default()).unwrap(); let res = stylesheet - .to_css(PrinterOptions { - project_root, - ..PrinterOptions::default() - }) + .to_css( + PrinterOptions { + project_root, + ..PrinterOptions::default() + }, + None::<&mut ()>, + ) .unwrap(); (res.code, res.exports.unwrap()) } fn bundle_custom_media(fs: P, entry: &str) -> String { - let mut bundler = Bundler::new( + let mut bundler = Bundler::<_, _, ()>::new( &fs, None, ParserOptions { @@ -1021,10 +1025,13 @@ mod tests { }) .unwrap(); stylesheet - .to_css(PrinterOptions { - targets, - ..PrinterOptions::default() - }) + .to_css( + PrinterOptions { + targets, + ..PrinterOptions::default() + }, + None::<&mut ()>, + ) .unwrap() .code } @@ -1034,7 +1041,7 @@ mod tests { entry: &str, maybe_cb: Option) -> ()>>, ) { - let mut bundler = Bundler::new(&fs, None, ParserOptions::default()); + let mut bundler = Bundler::<_, _, ()>::new(&fs, None, ParserOptions::default()); let res = bundler.bundle(Path::new(entry)); match res { Ok(_) => unreachable!(), @@ -2161,6 +2168,7 @@ mod tests { } #[test] + #[cfg(feature = "sourcemap")] fn test_source_map() { let source = r#".imported { content: "yay, file support!"; @@ -2192,11 +2200,13 @@ mod tests { let mut stylesheet = bundler.bundle(Path::new("/a.css")).unwrap(); stylesheet.minify(MinifyOptions::default()).unwrap(); stylesheet - .to_css(PrinterOptions { - source_map: Some(&mut sm), - minify: true, - ..PrinterOptions::default() - }) + .to_css( + PrinterOptions { + minify: true, + ..PrinterOptions::default() + }, + Some(&mut sm), + ) .unwrap(); let map = sm.to_json(None).unwrap(); assert_eq!( diff --git a/src/declaration.rs b/src/declaration.rs index 85a956318..36f30cacd 100644 --- a/src/declaration.rs +++ b/src/declaration.rs @@ -6,7 +6,6 @@ use std::ops::Range; use crate::context::{DeclarationContext, PropertyHandlerContext}; use crate::error::{ParserError, PrinterError, PrinterErrorKind}; use crate::parser::ParserOptions; -use crate::printer::Printer; use crate::properties::box_shadow::BoxShadowHandler; use crate::properties::custom::{CustomProperty, CustomPropertyName}; use crate::properties::masking::MaskHandler; @@ -124,10 +123,7 @@ impl<'i> DeclarationBlock<'i> { } impl<'i> ToCss for DeclarationBlock<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { let len = self.declarations.len() + self.important_declarations.len(); let mut i = 0; @@ -152,10 +148,10 @@ impl<'i> ToCss for DeclarationBlock<'i> { impl<'i> DeclarationBlock<'i> { /// Writes the declarations to a CSS block, including starting and ending braces. - pub fn to_css_block(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + pub fn to_css_block( + &self, + dest: &mut PrinterT, + ) -> Result<(), PrinterError> { dest.whitespace()?; dest.write_char('{')?; dest.indent(); @@ -165,7 +161,8 @@ impl<'i> DeclarationBlock<'i> { dest.dedent(); dest.newline()?; - dest.write_char('}') + dest.write_char('}')?; + Ok(()) } pub(crate) fn has_printable_declarations(&self) -> bool { @@ -183,16 +180,13 @@ impl<'i> DeclarationBlock<'i> { } /// Writes the declarations to a CSS declaration block. - pub fn to_css_declarations( + pub fn to_css_declarations( &self, - dest: &mut Printer, + dest: &mut PrinterT, has_nested_rules: bool, selectors: &SelectorList, source_index: u32, - ) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + ) -> Result<(), PrinterError> { let mut i = 0; let len = self.len(); @@ -202,11 +196,11 @@ impl<'i> DeclarationBlock<'i> { // The CSS modules `composes` property is handled specially, and omitted during printing. // We need to add the classes it references to the list for the selectors in this rule. if let crate::properties::Property::Composes(composes) = &decl { - if dest.is_nested() && dest.css_module.is_some() { + if dest.state().indent > 2 && dest.state().css_module.as_ref().is_some() { return Err(dest.error(PrinterErrorKind::InvalidComposesNesting, composes.loc)); } - if let Some(css_module) = &mut dest.css_module { + if let Some(css_module) = dest.state_mut().css_module.as_mut() { css_module .handle_composes(&selectors, &composes, source_index) .map_err(|e| dest.error(e, composes.loc))?; @@ -219,7 +213,7 @@ impl<'i> DeclarationBlock<'i> { } decl.to_css(dest, $important)?; - if i != len - 1 || !dest.minify || has_nested_rules { + if i != len - 1 || !dest.options().minify || has_nested_rules { dest.write_char(';')?; } diff --git a/src/lib.rs b/src/lib.rs index 2005a6daa..262ccdda1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -56,6 +56,7 @@ mod tests { use crate::dependencies::Dependency; use crate::error::{Error, ErrorLocation, MinifyErrorKind, ParserError, PrinterErrorKind, SelectorError}; use crate::parser::ParserFlags; + use crate::printer::{OriginalLocation, SourceMap}; use crate::properties::custom::Token; use crate::properties::Property; use crate::rules::CssRule; @@ -137,7 +138,7 @@ mod tests { if let Err(e) = stylesheet.minify(MinifyOptions::default()) { panic_with_test_error("test_with_options", "minify", source, e); } - let res = match stylesheet.to_css(PrinterOptions::default()) { + let res = match stylesheet.to_css(PrinterOptions::default(), None::<&mut ()>) { Ok(res) => res, Err(e) => panic_with_test_error("test_with_options", "print", source, e), }; @@ -153,7 +154,7 @@ mod tests { if let Err(e) = stylesheet.minify(MinifyOptions::default()) { panic_with_test_error("test_with_printer_options", "minify", source, e); } - let res = match stylesheet.to_css(options) { + let res = match stylesheet.to_css(options, None::<&mut ()>) { Ok(res) => res, Err(e) => panic_with_test_error("test_with_printer_options", "print", source, e), }; @@ -188,11 +189,14 @@ mod tests { }) { panic_with_test_error("minify_test_with_options", "minify", source, e); } - let res = match stylesheet.to_css(PrinterOptions { - minify: true, - targets, - ..PrinterOptions::default() - }) { + let res = match stylesheet.to_css( + PrinterOptions { + minify: true, + targets, + ..PrinterOptions::default() + }, + None::<&mut ()>, + ) { Ok(res) => res, Err(e) => panic_with_test_error("minify_test_with_options", "print", source, e), }; @@ -226,10 +230,13 @@ mod tests { }) { panic_with_test_error("prefix_test", "minify", source, e); } - let res = match stylesheet.to_css(PrinterOptions { - targets: targets.into(), - ..PrinterOptions::default() - }) { + let res = match stylesheet.to_css( + PrinterOptions { + targets: targets.into(), + ..PrinterOptions::default() + }, + None::<&mut ()>, + ) { Ok(res) => res, Err(e) => panic_with_test_error("prefix_test", "print", source, e), }; @@ -282,10 +289,13 @@ mod tests { }) { panic_with_test_error("nesting_test_with_targets", "minify", source, e); } - let res = match stylesheet.to_css(PrinterOptions { - targets, - ..PrinterOptions::default() - }) { + let res = match stylesheet.to_css( + PrinterOptions { + targets, + ..PrinterOptions::default() + }, + None::<&mut ()>, + ) { Ok(res) => res, Err(e) => panic_with_test_error("nesting_test_with_targets", "print", source, e), }; @@ -301,7 +311,7 @@ mod tests { if let Err(e) = stylesheet.minify(MinifyOptions::default()) { panic_with_test_error("nesting_test_no_targets", "minify", source, e); } - let res = match stylesheet.to_css(PrinterOptions::default()) { + let res = match stylesheet.to_css(PrinterOptions::default(), None::<&mut ()>) { Ok(res) => res, Err(e) => panic_with_test_error("nesting_test_no_targets", "print", source, e), }; @@ -366,10 +376,13 @@ mod tests { if let Err(e) = stylesheet.minify(MinifyOptions::default()) { panic_with_test_error("css_modules_test", "minify", source, e); } - let res = match stylesheet.to_css(PrinterOptions { - minify, - ..Default::default() - }) { + let res = match stylesheet.to_css( + PrinterOptions { + minify, + ..Default::default() + }, + None::<&mut ()>, + ) { Ok(res) => res, Err(e) => panic_with_test_error("css_modules_test", "print", source, e), }; @@ -406,7 +419,7 @@ mod tests { }) { panic_with_test_error("custom_media_test", "minify", source, e); } - let res = match stylesheet.to_css(PrinterOptions::default()) { + let res = match stylesheet.to_css(PrinterOptions::default(), None::<&mut ()>) { Ok(res) => res, Err(e) => panic_with_test_error("custom_media_test", "print", source, e), }; @@ -27021,10 +27034,13 @@ mod tests { .unwrap(); stylesheet.minify(MinifyOptions::default()).unwrap(); let res = stylesheet - .to_css(PrinterOptions { - minify: true, - ..PrinterOptions::default() - }) + .to_css( + PrinterOptions { + minify: true, + ..PrinterOptions::default() + }, + None::<&mut ()>, + ) .unwrap(); assert_eq!(res.code, ".foo{color:#00f;& .bar{color:red}}"); @@ -27728,7 +27744,7 @@ mod tests { }, ) .unwrap(); - if let Err(err) = stylesheet.to_css(PrinterOptions::default()) { + if let Err(err) = stylesheet.to_css(PrinterOptions::default(), None::<&mut ()>) { assert_eq!(err.kind, PrinterErrorKind::InvalidCssModulesPatternInGrid); } else { unreachable!() @@ -28186,10 +28202,13 @@ mod tests { ) .unwrap(); let res = stylesheet - .to_css(PrinterOptions { - project_root: Some(project_root), - ..PrinterOptions::default() - }) + .to_css( + PrinterOptions { + project_root: Some(project_root), + ..PrinterOptions::default() + }, + None::<&mut ()>, + ) .unwrap(); assert_eq!( res.code, @@ -28228,14 +28247,17 @@ mod tests { .unwrap(); stylesheet.minify(MinifyOptions::default()).unwrap(); let res = stylesheet - .to_css(PrinterOptions { - targets: Browsers { - chrome: Some(95 << 16), - ..Browsers::default() - } - .into(), - ..Default::default() - }) + .to_css( + PrinterOptions { + targets: Browsers { + chrome: Some(95 << 16), + ..Browsers::default() + } + .into(), + ..Default::default() + }, + None::<&mut ()>, + ) .unwrap(); assert_eq!( res.code, @@ -28292,15 +28314,18 @@ mod tests { let stylesheet = StyleSheet::parse(&source, ParserOptions::default()).unwrap(); let res = stylesheet - .to_css(PrinterOptions { - pseudo_classes: Some(PseudoClasses { - hover: Some("is-hovered"), - active: Some("is-active"), - focus_visible: Some("focus-visible"), - ..PseudoClasses::default() - }), - ..PrinterOptions::default() - }) + .to_css( + PrinterOptions { + pseudo_classes: Some(PseudoClasses { + hover: Some("is-hovered"), + active: Some("is-active"), + focus_visible: Some("focus-visible"), + ..PseudoClasses::default() + }), + ..PrinterOptions::default() + }, + None::<&mut ()>, + ) .unwrap(); assert_eq!(res.code, expected); @@ -28326,13 +28351,16 @@ mod tests { ) .unwrap(); let res = stylesheet - .to_css(PrinterOptions { - pseudo_classes: Some(PseudoClasses { - hover: Some("is-hovered"), - ..PseudoClasses::default() - }), - ..PrinterOptions::default() - }) + .to_css( + PrinterOptions { + pseudo_classes: Some(PseudoClasses { + hover: Some("is-hovered"), + ..PseudoClasses::default() + }), + ..PrinterOptions::default() + }, + None::<&mut ()>, + ) .unwrap(); assert_eq!(res.code, expected); } @@ -28413,7 +28441,7 @@ mod tests { ..MinifyOptions::default() }) .unwrap(); - let res = stylesheet.to_css(PrinterOptions::default()).unwrap(); + let res = stylesheet.to_css(PrinterOptions::default(), None::<&mut ()>).unwrap(); assert_eq!(res.code, expected); let source = r#" @@ -28439,7 +28467,7 @@ mod tests { ..MinifyOptions::default() }) .unwrap(); - let res = stylesheet.to_css(PrinterOptions::default()).unwrap(); + let res = stylesheet.to_css(PrinterOptions::default(), None::<&mut ()>).unwrap(); assert_eq!(res.code, expected); let source = r#" @@ -28486,14 +28514,17 @@ mod tests { }) .unwrap(); let res = stylesheet - .to_css(PrinterOptions { - targets: Browsers { - chrome: Some(95 << 16), - ..Browsers::default() - } - .into(), - ..PrinterOptions::default() - }) + .to_css( + PrinterOptions { + targets: Browsers { + chrome: Some(95 << 16), + ..Browsers::default() + } + .into(), + ..PrinterOptions::default() + }, + None::<&mut ()>, + ) .unwrap(); assert_eq!(res.code, expected); @@ -28535,7 +28566,7 @@ mod tests { ..MinifyOptions::default() }) .unwrap(); - let res = stylesheet.to_css(PrinterOptions::default()).unwrap(); + let res = stylesheet.to_css(PrinterOptions::default(), None::<&mut ()>).unwrap(); assert_eq!(res.code, expected); } @@ -30379,11 +30410,14 @@ mod tests { .unwrap(); stylesheet.minify(MinifyOptions::default()).unwrap(); let res = stylesheet - .to_css(PrinterOptions { - analyze_dependencies: Some(Default::default()), - minify: true, - ..PrinterOptions::default() - }) + .to_css( + PrinterOptions { + analyze_dependencies: Some(Default::default()), + minify: true, + ..PrinterOptions::default() + }, + None::<&mut ()>, + ) .unwrap(); assert_eq!(res.code, expected); let dependencies = res.dependencies.unwrap(); @@ -30404,10 +30438,13 @@ mod tests { fn dep_error_test(source: &str, error: PrinterErrorKind) { let stylesheet = StyleSheet::parse(&source, ParserOptions::default()).unwrap(); - let res = stylesheet.to_css(PrinterOptions { - analyze_dependencies: Some(Default::default()), - ..PrinterOptions::default() - }); + let res = stylesheet.to_css( + PrinterOptions { + analyze_dependencies: Some(Default::default()), + ..PrinterOptions::default() + }, + None::<&mut ()>, + ); match res { Err(e) => assert_eq!(e.kind, error), _ => unreachable!(), @@ -31165,6 +31202,94 @@ mod tests { minify_test(".foo { z-index: -9999999 }", ".foo{z-index:-9999999}"); } + #[derive(Default)] + struct TestSourceMap { + sources: Vec, + names: Vec, + sources_content: Vec<(u32, String)>, + mappings: Vec<(u32, u32, Option)>, + } + + impl SourceMap for TestSourceMap { + fn add_source(&mut self, source: &str) -> u32 { + if let Some(index) = self.sources.iter().position(|s| s == source) { + index as u32 + } else { + self.sources.push(source.to_owned()); + (self.sources.len() - 1) as u32 + } + } + + fn add_name(&mut self, name: &str) -> u32 { + if let Some(index) = self.names.iter().position(|s| s == name) { + index as u32 + } else { + self.names.push(name.to_owned()); + (self.names.len() - 1) as u32 + } + } + + fn set_source_content(&mut self, source_index: u32, source_content: &str) { + self.sources_content.push((source_index, source_content.to_owned())); + } + + fn add_mapping(&mut self, generated_line: u32, generated_column: u32, original: Option) { + self.mappings.push((generated_line, generated_column, original)); + } + + fn from_data_url(_source_root: &str, _data_url: &str) -> Option { + None + } + + fn find_closest_mapping(&mut self, _line: u32, _column: u32) -> Option { + None + } + + fn get_source(&self, _source_index: u32) -> Option<&str> { + None + } + + fn get_name(&self, _name_index: u32) -> Option<&str> { + None + } + + fn get_source_content(&self, _source_index: u32) -> Option<&str> { + None + } + } + + #[test] + fn test_custom_source_map() { + let mut stylesheet = StyleSheet::parse(".foo { color: red }", ParserOptions::default()).unwrap(); + stylesheet.minify(MinifyOptions::default()).unwrap(); + + let mut source_map = TestSourceMap::default(); + let result = stylesheet + .to_css( + PrinterOptions { + minify: true, + ..PrinterOptions::default() + }, + Some(&mut source_map), + ) + .unwrap(); + + assert_eq!(result.code, ".foo{color:red}"); + assert_eq!( + source_map.mappings.first(), + Some(&( + 0, + 0, + Some(OriginalLocation { + original_line: 0, + original_column: 0, + source: 0, + name: None, + }) + )) + ); + } + #[test] #[cfg(feature = "sourcemap")] fn test_input_source_map() { @@ -31187,11 +31312,13 @@ mod tests { stylesheet.minify(MinifyOptions::default()).unwrap(); let mut sm = parcel_sourcemap::SourceMap::new("/"); stylesheet - .to_css(PrinterOptions { - source_map: Some(&mut sm), - minify: true, - ..PrinterOptions::default() - }) + .to_css( + PrinterOptions { + minify: true, + ..PrinterOptions::default() + }, + Some(&mut sm), + ) .unwrap(); let map = sm.to_json(None).unwrap(); assert_eq!( @@ -31233,11 +31360,13 @@ mod tests { .unwrap(); stylesheet.minify(MinifyOptions::default()).unwrap(); stylesheet - .to_css(PrinterOptions { - source_map: Some(&mut sm), - minify: true, - ..PrinterOptions::default() - }) + .to_css( + PrinterOptions { + minify: true, + ..PrinterOptions::default() + }, + Some(&mut sm), + ) .unwrap(); let map = sm.to_json(None).unwrap(); assert_eq!( diff --git a/src/macros.rs b/src/macros.rs index a50d54f5a..f397efc91 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -83,8 +83,9 @@ macro_rules! enum_property { } impl ToCss for $name { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> where W: std::fmt::Write { - dest.write_str(self.as_str()) + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { + dest.write_str(self.as_str())?; + Ok(()) } } }; @@ -152,7 +153,7 @@ macro_rules! shorthand_property { } impl$(<$l>)? ToCss for $name$(<$l>)? { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> where W: std::fmt::Write { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { let mut needs_space = false; macro_rules! print_one { ($k: ident, $t: ty) => { @@ -755,10 +756,7 @@ macro_rules! rect_shorthand { } impl ToCss for $name { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { Rect::new(&self.top, &self.right, &self.bottom, &self.left).to_css(dest) } } @@ -798,10 +796,7 @@ macro_rules! size_shorthand { } impl ToCss for $name { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { Size2D(&self.$a_key, &self.$b_key).to_css(dest) } } diff --git a/src/main.rs b/src/main.rs index a3ff7e520..27f9c8a51 100644 --- a/src/main.rs +++ b/src/main.rs @@ -213,13 +213,15 @@ pub fn main() -> Result<(), std::io::Error> { .unwrap(); stylesheet - .to_css(PrinterOptions { - minify: cli_args.minify, - source_map: source_map.as_mut(), - project_root: Some(&project_root.to_string_lossy()), - targets, - ..PrinterOptions::default() - }) + .to_css( + PrinterOptions { + minify: cli_args.minify, + project_root: Some(&project_root.to_string_lossy()), + targets, + ..PrinterOptions::default() + }, + source_map.as_mut(), + ) .unwrap() }; diff --git a/src/media_query.rs b/src/media_query.rs index e95a0c172..3970b07d7 100644 --- a/src/media_query.rs +++ b/src/media_query.rs @@ -2,7 +2,6 @@ use crate::error::{ErrorWithLocation, MinifyError, MinifyErrorKind, ParserError, PrinterError}; use crate::macros::enum_property; use crate::parser::starts_with_ignore_ascii_case; -use crate::printer::Printer; use crate::properties::custom::{EnvironmentVariable, TokenList}; #[cfg(feature = "visitor")] use crate::rules::container::{ContainerSizeFeatureId, ScrollStateFeatureId}; @@ -160,10 +159,7 @@ impl<'i> MediaList<'i> { } impl<'i> ToCss for MediaList<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { if self.media_queries.is_empty() { dest.write_str("not all")?; return Ok(()); @@ -418,10 +414,7 @@ impl<'i> MediaQuery<'i> { } impl<'i> ToCss for MediaQuery<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { if let Some(qual) = self.qualifier { qual.to_css(dest)?; dest.write_char(' ')?; @@ -810,14 +803,11 @@ fn parse_paren_block<'t, 'i, P: QueryCondition<'i>>( }) } -pub(crate) fn to_css_with_parens_if_needed( +pub(crate) fn to_css_with_parens_if_needed( value: V, - dest: &mut Printer, + dest: &mut PrinterT, needs_parens: bool, -) -> Result<(), PrinterError> -where - W: std::fmt::Write, -{ +) -> Result<(), PrinterError> { if needs_parens { dest.write_char('(')?; } @@ -828,22 +818,27 @@ where Ok(()) } -pub(crate) fn operation_to_css<'i, V: ToCss + QueryCondition<'i>, W>( +pub(crate) fn operation_to_css<'i, V: ToCss + QueryCondition<'i>, PrinterT: crate::printer::PrinterTrait>( operator: Operator, conditions: &Vec, - dest: &mut Printer, -) -> Result<(), PrinterError> -where - W: std::fmt::Write, -{ + dest: &mut PrinterT, +) -> Result<(), PrinterError> { let mut iter = conditions.iter(); let first = iter.next().unwrap(); - to_css_with_parens_if_needed(first, dest, first.needs_parens(Some(operator), &dest.targets.current))?; + to_css_with_parens_if_needed( + first, + dest, + first.needs_parens(Some(operator), &dest.state().targets.current), + )?; for item in iter { dest.write_char(' ')?; operator.to_css(dest)?; dest.write_char(' ')?; - to_css_with_parens_if_needed(item, dest, item.needs_parens(Some(operator), &dest.targets.current))?; + to_css_with_parens_if_needed( + item, + dest, + item.needs_parens(Some(operator), &dest.state().targets.current), + )?; } Ok(()) @@ -860,10 +855,7 @@ impl<'i> MediaCondition<'i> { } impl<'i> ToCss for MediaCondition<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match *self { MediaCondition::Feature(ref f) => f.to_css(dest), MediaCondition::Not(ref c) => { @@ -871,7 +863,7 @@ impl<'i> ToCss for MediaCondition<'i> { negated.to_css(dest) } else { dest.write_str("not ")?; - to_css_with_parens_if_needed(&**c, dest, c.needs_parens(None, &dest.targets.current)) + to_css_with_parens_if_needed(&**c, dest, c.needs_parens(None, &dest.state().targets.current)) } } MediaCondition::Operation { @@ -907,10 +899,7 @@ pub enum MediaFeatureComparison { } impl ToCss for MediaFeatureComparison { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { use MediaFeatureComparison::*; match self { Equal => dest.delim('=', true), @@ -1168,10 +1157,7 @@ where } impl<'i, FeatureId: FeatureToCss> ToCss for QueryFeature<'i, FeatureId> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { QueryFeature::Boolean { name } => { dest.write_char('(')?; @@ -1185,7 +1171,7 @@ impl<'i, FeatureId: FeatureToCss> ToCss for QueryFeature<'i, FeatureId> { } QueryFeature::Range { name, operator, value } => { // If range syntax is unsupported, use min/max prefix if possible. - if should_compile!(dest.targets.current, MediaRangeSyntax) { + if should_compile!(dest.state().targets.current, MediaRangeSyntax) { return write_min_max(operator, name, value, dest, false); } @@ -1201,7 +1187,7 @@ impl<'i, FeatureId: FeatureToCss> ToCss for QueryFeature<'i, FeatureId> { end, end_operator, } => { - if should_compile!(dest.targets.current, MediaIntervalSyntax) { + if should_compile!(dest.state().targets.current, MediaIntervalSyntax) { write_min_max(&start_operator.opposite(), name, start, dest, true)?; dest.write_str(" and ")?; return write_min_max(end_operator, name, end, dest, true); @@ -1216,7 +1202,9 @@ impl<'i, FeatureId: FeatureToCss> ToCss for QueryFeature<'i, FeatureId> { } } - dest.write_char(')') + dest.write_char(')')?; + + Ok(()) } } @@ -1302,10 +1290,7 @@ impl<'i, FeatureId: ValueType> ValueType for MediaFeatureName<'i, FeatureId> { } impl<'i, FeatureId: FeatureToCss> ToCss for MediaFeatureName<'i, FeatureId> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { Self::Standard(v) => v.to_css(dest), Self::Custom(v) => v.to_css(dest), @@ -1315,10 +1300,11 @@ impl<'i, FeatureId: FeatureToCss> ToCss for MediaFeatureName<'i, FeatureId> { } impl<'i, FeatureId: FeatureToCss> FeatureToCss for MediaFeatureName<'i, FeatureId> { - fn to_css_with_prefix(&self, prefix: &str, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css_with_prefix( + &self, + prefix: &str, + dest: &mut PrinterT, + ) -> Result<(), PrinterError> { match self { Self::Standard(v) => v.to_css_with_prefix(prefix, dest), Self::Custom(v) => { @@ -1494,21 +1480,25 @@ define_query_features! { } pub(crate) trait FeatureToCss: ToCss { - fn to_css_with_prefix(&self, prefix: &str, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write; + fn to_css_with_prefix( + &self, + prefix: &str, + dest: &mut PrinterT, + ) -> Result<(), PrinterError>; } impl FeatureToCss for MediaFeatureId { - fn to_css_with_prefix(&self, prefix: &str, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css_with_prefix( + &self, + prefix: &str, + dest: &mut PrinterT, + ) -> Result<(), PrinterError> { match self { MediaFeatureId::WebKitDevicePixelRatio => { dest.write_str("-webkit-")?; dest.write_str(prefix)?; - dest.write_str("device-pixel-ratio") + dest.write_str("device-pixel-ratio")?; + Ok(()) } _ => { dest.write_str(prefix)?; @@ -1519,16 +1509,13 @@ impl FeatureToCss for MediaFeatureId { } #[inline] -fn write_min_max( +fn write_min_max( operator: &MediaFeatureComparison, name: &MediaFeatureName, value: &MediaFeatureValue, - dest: &mut Printer, + dest: &mut PrinterT, is_range: bool, -) -> Result<(), PrinterError> -where - W: std::fmt::Write, -{ +) -> Result<(), PrinterError> { let prefix = match operator { MediaFeatureComparison::GreaterThan => { if is_range { @@ -1694,19 +1681,18 @@ impl<'i> MediaFeatureValue<'i> { } impl<'i> ToCss for MediaFeatureValue<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { MediaFeatureValue::Length(len) => len.to_css(dest), MediaFeatureValue::Number(num) => num.to_css(dest), MediaFeatureValue::Integer(num) => num.to_css(dest), MediaFeatureValue::Boolean(b) => { if *b { - dest.write_char('1') + dest.write_char('1')?; + Ok(()) } else { - dest.write_char('0') + dest.write_char('0')?; + Ok(()) } } MediaFeatureValue::Resolution(res) => res.to_css(dest), diff --git a/src/parser.rs b/src/parser.rs index 51bbf9d91..26cf8961b 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,7 +1,6 @@ use crate::declaration::{parse_declaration, DeclarationBlock, DeclarationList}; use crate::error::{Error, ParserError, PrinterError}; use crate::media_query::*; -use crate::printer::Printer; use crate::properties::custom::TokenList; use crate::rules::container::{ContainerCondition, ContainerName, ContainerRule}; use crate::rules::font_feature_values::FontFeatureValuesRule; @@ -124,7 +123,7 @@ impl<'i> crate::traits::AtRuleParser<'i> for DefaultAtRuleParser { #[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))] pub struct DefaultAtRule; impl crate::traits::ToCss for DefaultAtRule { - fn to_css(&self, _: &mut Printer) -> Result<(), PrinterError> { + fn to_css(&self, _: &mut PrinterT) -> Result<(), PrinterError> { Err(PrinterError { kind: crate::error::PrinterErrorKind::FmtError, loc: None, diff --git a/src/printer.rs b/src/printer.rs index 9086b9f40..685bcae80 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -3,23 +3,211 @@ use crate::css_modules::CssModule; use crate::dependencies::{Dependency, DependencyOptions}; use crate::error::{Error, ErrorLocation, PrinterError, PrinterErrorKind}; -use crate::rules::{Location, StyleContext}; -use crate::selector::SelectorList; +use crate::rules::{Location, StyleContextPtr}; use crate::targets::{Targets, TargetsWithSupportsScope}; use crate::vendor_prefix::VendorPrefix; use cssparser::{serialize_identifier, serialize_name}; #[cfg(feature = "sourcemap")] -use parcel_sourcemap::{OriginalLocation, SourceMap}; +use parcel_sourcemap::SourceMap as ParcelSourceMap; +#[cfg(not(feature = "custom_sourcemap"))] +use std::marker::PhantomData; + +/// An original source location referenced by a generated source map mapping. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct OriginalLocation { + /// The zero-based line number in the original source. + pub original_line: u32, + /// The zero-based column number in the original source. + pub original_column: u32, + /// The source index in the source map. + pub source: u32, + /// The optional name index in the source map. + pub name: Option, +} + +/// A source map implementation that can receive generated mappings and +/// provide input mappings for remapping. +#[cfg(feature = "custom_sourcemap")] +pub trait SourceMap { + /// Adds a source path and returns its source index. + fn add_source(&mut self, source: &str) -> u32; + + /// Adds a symbol name and returns its name index. + fn add_name(&mut self, name: &str) -> u32; + + /// Sets the original content for a source index. + fn set_source_content(&mut self, source_index: u32, source_content: &str); + + /// Adds a mapping from a generated position to an optional original location. + fn add_mapping(&mut self, generated_line: u32, generated_column: u32, original: Option); + + /// Reads an inline source map data URL. + fn from_data_url(source_root: &str, data_url: &str) -> Option + where + Self: Sized; + + /// Finds the closest original location for a generated position. + fn find_closest_mapping(&mut self, line: u32, column: u32) -> Option; + + /// Returns the source path for a source index. + fn get_source(&self, source_index: u32) -> Option<&str>; + + /// Returns the symbol name for a name index. + fn get_name(&self, name_index: u32) -> Option<&str>; + + /// Returns the source content for a source index. + fn get_source_content(&self, source_index: u32) -> Option<&str>; +} + +/// A source map placeholder used when custom source maps are disabled. +#[cfg(not(feature = "custom_sourcemap"))] +pub trait SourceMap {} + +#[cfg(not(feature = "custom_sourcemap"))] +impl SourceMap for () {} + +#[cfg(feature = "custom_sourcemap")] +impl SourceMap for () { + fn add_source(&mut self, _source: &str) -> u32 { + unreachable!() + } + + fn add_name(&mut self, _name: &str) -> u32 { + unreachable!() + } + + fn set_source_content(&mut self, _source_index: u32, _source_content: &str) { + unreachable!() + } + + fn add_mapping(&mut self, _generated_line: u32, _generated_column: u32, _original: Option) { + unreachable!() + } + + fn from_data_url(_source_root: &str, _data_url: &str) -> Option { + unreachable!() + } + + fn find_closest_mapping(&mut self, _line: u32, _column: u32) -> Option { + unreachable!() + } + + fn get_source(&self, _source_index: u32) -> Option<&str> { + unreachable!() + } + + fn get_name(&self, _name_index: u32) -> Option<&str> { + unreachable!() + } + + fn get_source_content(&self, _source_index: u32) -> Option<&str> { + unreachable!() + } +} + +#[cfg(feature = "custom_sourcemap")] +impl SourceMap for &mut T { + fn add_source(&mut self, source: &str) -> u32 { + (**self).add_source(source) + } + + fn add_name(&mut self, name: &str) -> u32 { + (**self).add_name(name) + } + + fn set_source_content(&mut self, source_index: u32, source_content: &str) { + (**self).set_source_content(source_index, source_content) + } + + fn add_mapping(&mut self, generated_line: u32, generated_column: u32, original: Option) { + (**self).add_mapping(generated_line, generated_column, original) + } + + fn from_data_url(source_root: &str, data_url: &str) -> Option + where + Self: Sized, + { + let _ = (source_root, data_url); + None + } + + fn find_closest_mapping(&mut self, line: u32, column: u32) -> Option { + (**self).find_closest_mapping(line, column) + } + + fn get_source(&self, source_index: u32) -> Option<&str> { + (**self).get_source(source_index) + } + + fn get_name(&self, name_index: u32) -> Option<&str> { + (**self).get_name(name_index) + } + + fn get_source_content(&self, source_index: u32) -> Option<&str> { + (**self).get_source_content(source_index) + } +} + +#[cfg(feature = "sourcemap")] +#[cfg_attr(docsrs, doc(cfg(feature = "sourcemap")))] +impl SourceMap for ParcelSourceMap { + fn add_source(&mut self, source: &str) -> u32 { + ParcelSourceMap::add_source(self, source) + } + + fn add_name(&mut self, name: &str) -> u32 { + ParcelSourceMap::add_name(self, name) + } + + fn set_source_content(&mut self, source_index: u32, source_content: &str) { + let _ = ParcelSourceMap::set_source_content(self, source_index as usize, source_content); + } + + fn add_mapping(&mut self, generated_line: u32, generated_column: u32, original: Option) { + ParcelSourceMap::add_mapping( + self, + generated_line, + generated_column, + original.map(|original| parcel_sourcemap::OriginalLocation { + original_line: original.original_line, + original_column: original.original_column, + source: original.source, + name: original.name, + }), + ); + } + + fn from_data_url(source_root: &str, data_url: &str) -> Option { + ParcelSourceMap::from_data_url(source_root, data_url).ok() + } + + fn find_closest_mapping(&mut self, line: u32, column: u32) -> Option { + let mapping = ParcelSourceMap::find_closest_mapping(self, line, column)?; + mapping.original.map(|original| OriginalLocation { + original_line: original.original_line, + original_column: original.original_column, + source: original.source, + name: original.name, + }) + } + + fn get_source(&self, source_index: u32) -> Option<&str> { + ParcelSourceMap::get_source(self, source_index).ok() + } + + fn get_name(&self, name_index: u32) -> Option<&str> { + ParcelSourceMap::get_name(self, name_index).ok() + } + + fn get_source_content(&self, source_index: u32) -> Option<&str> { + ParcelSourceMap::get_source_content(self, source_index).ok() + } +} /// Options that control how CSS is serialized to a string. -#[derive(Default)] pub struct PrinterOptions<'a> { /// Whether to minify the CSS, i.e. remove white space. pub minify: bool, - /// An optional reference to a source map to write mappings into. - #[cfg(feature = "sourcemap")] - #[cfg_attr(docsrs, doc(cfg(feature = "sourcemap")))] - pub source_map: Option<&'a mut SourceMap>, /// An optional project root path, used to generate relative paths for sources used in CSS module hashes. pub project_root: Option<&'a str>, /// Targets to output the CSS for. @@ -37,6 +225,33 @@ pub struct PrinterOptions<'a> { pub pseudo_classes: Option>, } +impl<'a> Default for PrinterOptions<'a> { + fn default() -> Self { + PrinterOptions { + minify: false, + project_root: None, + targets: Targets::default(), + analyze_dependencies: None, + pseudo_classes: None, + } + } +} + +#[allow(missing_docs)] +pub struct PrinterState<'a, 'c> { + pub(crate) loc: Location, + pub(crate) generated_line: u32, + pub(crate) generated_col: u32, + pub(crate) indent: u8, + pub(crate) targets: TargetsWithSupportsScope, + pub(crate) vendor_prefix: VendorPrefix, + pub(crate) in_calc: bool, + pub(crate) css_module: Option>, + pub(crate) dependencies: Option>, + pub(crate) remove_imports: bool, + pub(crate) context: Option, +} + /// A mapping of user action pseudo classes to replace with class names. /// /// See [PrinterOptions](PrinterOptions). @@ -59,74 +274,85 @@ pub struct PseudoClasses<'a> { /// implements [std::fmt::Write](std::fmt::Write), such as a [String](String). /// /// A `Printer` keeps track of the current line and column position, and uses -/// this to generate a source map if provided in the options. +/// this to generate a source map if one is provided. /// /// `Printer` also includes helper functions that assist with writing output /// that respects options such as `minify`, and `css_modules`. -pub struct Printer<'a, 'c, W> { +pub struct Printer<'a, 'c, W, S: SourceMap = ()> { pub(crate) sources: Option<&'c Vec>, dest: &'a mut W, - #[cfg(feature = "sourcemap")] - #[cfg_attr(docsrs, doc(cfg(feature = "sourcemap")))] - pub(crate) source_map: Option<&'a mut SourceMap>, - #[cfg(feature = "sourcemap")] - #[cfg_attr(docsrs, doc(cfg(feature = "sourcemap")))] - pub(crate) source_maps: Vec>, - pub(crate) loc: Location, - indent: u8, - line: u32, - col: u32, - pub(crate) minify: bool, - pub(crate) targets: TargetsWithSupportsScope, - /// Vendor prefix override. When non-empty, it overrides - /// the vendor prefix of whatever is being printed. - pub(crate) vendor_prefix: VendorPrefix, - pub(crate) in_calc: bool, - pub(crate) css_module: Option>, - pub(crate) dependencies: Option>, - pub(crate) remove_imports: bool, - pub(crate) pseudo_classes: Option>, - context: Option<&'a StyleContext<'a, 'c>>, + pub(crate) options: PrinterOptions<'a>, + pub(crate) state: PrinterState<'a, 'c>, + #[cfg(feature = "custom_sourcemap")] + pub(crate) source_map: Option<&'a mut S>, + #[cfg(feature = "custom_sourcemap")] + pub(crate) source_maps: Vec>, + #[cfg(not(feature = "custom_sourcemap"))] + source_map: PhantomData S>, } -impl<'a, 'c, W: std::fmt::Write + Sized> Printer<'a, 'c, W> { +impl<'a, 'c, W: std::fmt::Write + Sized> Printer<'a, 'c, W, ()> { /// Create a new Printer wrapping the given destination. pub fn new(dest: &'a mut W, options: PrinterOptions<'a>) -> Self { + Self::new_impl(dest, options) + } +} + +impl<'a, 'c, W: std::fmt::Write + Sized, S: SourceMap> Printer<'a, 'c, W, S> { + fn new_impl(dest: &'a mut W, options: PrinterOptions<'a>) -> Self { + let dependencies = if options.analyze_dependencies.is_some() { + Some(Vec::new()) + } else { + None + }; + let remove_imports = matches!(&options.analyze_dependencies, Some(d) if d.remove_imports); Printer { sources: None, dest, - #[cfg(feature = "sourcemap")] - source_map: options.source_map, - #[cfg(feature = "sourcemap")] - source_maps: Vec::new(), - loc: Location { - source_index: 0, - line: 0, - column: 1, - }, - indent: 0, - line: 0, - col: 0, - minify: options.minify, - targets: TargetsWithSupportsScope::new(options.targets), - vendor_prefix: VendorPrefix::empty(), - in_calc: false, - css_module: None, - dependencies: if options.analyze_dependencies.is_some() { - Some(Vec::new()) - } else { - None + #[cfg(feature = "custom_sourcemap")] + source_map: None, + state: PrinterState { + loc: Location { + source_index: 0, + line: 0, + column: 1, + }, + generated_line: 0, + generated_col: 0, + indent: 0, + targets: TargetsWithSupportsScope::new(options.targets), + vendor_prefix: VendorPrefix::empty(), + in_calc: false, + css_module: None, + dependencies, + remove_imports, + context: None, }, - remove_imports: matches!(&options.analyze_dependencies, Some(d) if d.remove_imports), - pseudo_classes: options.pseudo_classes, - context: None, + options, + #[cfg(feature = "custom_sourcemap")] + source_maps: Vec::new(), + #[cfg(not(feature = "custom_sourcemap"))] + source_map: PhantomData, + } + } + + #[cfg(feature = "custom_sourcemap")] + /// Attaches a source map writer to this printer. + pub fn with_source_map(self, source_map: Option<&'a mut T>) -> Printer<'a, 'c, W, T> { + Printer { + sources: self.sources, + dest: self.dest, + options: self.options, + state: self.state, + source_map, + source_maps: Vec::new(), } } /// Returns the current source filename that is being printed. pub fn filename(&self) -> &'c str { if let Some(sources) = self.sources { - if let Some(f) = sources.get(self.loc.source_index as usize) { + if let Some(f) = sources.get(self.state.loc.source_index as usize) { f } else { "unknown.css" @@ -141,7 +367,7 @@ impl<'a, 'c, W: std::fmt::Write + Sized> Printer<'a, 'c, W> { /// NOTE: Is is assumed that the string does not contain any newline characters. /// If such a string is written, it will break source maps. pub fn write_str(&mut self, s: &str) -> Result<(), PrinterError> { - self.col += s.len() as u32; + self.state.generated_col += s.len() as u32; self.dest.write_str(s)?; Ok(()) } @@ -152,15 +378,15 @@ impl<'a, 'c, W: std::fmt::Write + Sized> Printer<'a, 'c, W> { for (idx, n) in s.char_indices() { if n == '\n' { - self.line += 1; - self.col = 0; + self.state.generated_line += 1; + self.state.generated_col = 0; // Keep track of where the *next* line starts last_line_start = idx + 1; } } - self.col += (s.len() - last_line_start) as u32; + self.state.generated_col += (s.len() - last_line_start) as u32; self.dest.write_str(s)?; Ok(()) } @@ -168,10 +394,10 @@ impl<'a, 'c, W: std::fmt::Write + Sized> Printer<'a, 'c, W> { /// Write a single character to the underlying destination. pub fn write_char(&mut self, c: char) -> Result<(), PrinterError> { if c == '\n' { - self.line += 1; - self.col = 0; + self.state.generated_line += 1; + self.state.generated_col = 0; } else { - self.col += 1; + self.state.generated_col += 1; } self.dest.write_char(c)?; Ok(()) @@ -182,7 +408,7 @@ impl<'a, 'c, W: std::fmt::Write + Sized> Printer<'a, 'c, W> { /// Use `write_char` instead if you wish to force a space character to be written, /// regardless of the `minify` option. pub fn whitespace(&mut self) -> Result<(), PrinterError> { - if self.minify { + if self.options.minify { return Ok(()); } @@ -202,13 +428,13 @@ impl<'a, 'c, W: std::fmt::Write + Sized> Printer<'a, 'c, W> { /// Writes a newline character followed by indentation. /// If the `minify` option is enabled, then nothing is printed. pub fn newline(&mut self) -> Result<(), PrinterError> { - if self.minify { + if self.options.minify { return Ok(()); } self.write_char('\n')?; - if self.indent > 0 { - self.write_str(&" ".repeat(self.indent as usize))?; + if self.state.indent > 0 { + self.write_str(&" ".repeat(self.state.indent as usize))?; } Ok(()) @@ -216,72 +442,27 @@ impl<'a, 'c, W: std::fmt::Write + Sized> Printer<'a, 'c, W> { /// Increases the current indent level. pub fn indent(&mut self) { - self.indent += 2; + self.state.indent += 2; } /// Decreases the current indent level. pub fn dedent(&mut self) { - self.indent -= 2; + self.state.indent -= 2; } /// Increases the current indent level by the given number of characters. pub fn indent_by(&mut self, amt: u8) { - self.indent += amt; + self.state.indent += amt; } /// Decreases the current indent level by the given number of characters. pub fn dedent_by(&mut self, amt: u8) { - self.indent -= amt; + self.state.indent -= amt; } /// Returns whether the indent level is greater than one. pub fn is_nested(&self) -> bool { - self.indent > 2 - } - - /// Adds a mapping to the source map, if any. - #[cfg(feature = "sourcemap")] - #[cfg_attr(docsrs, doc(cfg(feature = "sourcemap")))] - pub fn add_mapping(&mut self, loc: Location) { - self.loc = loc; - - if let Some(map) = &mut self.source_map { - let mut original = OriginalLocation { - original_line: loc.line, - original_column: loc.column - 1, - source: loc.source_index, - name: None, - }; - - // Remap using input source map if possible. - if let Some(Some(sm)) = self.source_maps.get_mut(loc.source_index as usize) { - let mut found_mapping = false; - if let Some(mapping) = sm.find_closest_mapping(loc.line, loc.column - 1) { - if let Some(orig) = mapping.original { - let sources_len = map.get_sources().len(); - let source_index = map.add_source(sm.get_source(orig.source).unwrap()); - let name = orig.name.map(|name| map.add_name(sm.get_name(name).unwrap())); - original.original_line = orig.original_line; - original.original_column = orig.original_column; - original.source = source_index; - original.name = name; - - if map.get_sources().len() > sources_len { - let content = sm.get_source_content(orig.source).unwrap().to_owned(); - let _ = map.set_source_content(source_index as usize, &content); - } - - found_mapping = true; - } - } - - if !found_mapping { - return; - } - } - - map.add_mapping(self.line, self.col, Some(original)) - } + self.state.indent > 2 } /// Writes a CSS identifier to the underlying destination, escaping it @@ -289,20 +470,20 @@ impl<'a, 'c, W: std::fmt::Write + Sized> Printer<'a, 'c, W> { /// is added, and the mapping is added to the CSS module. pub fn write_ident(&mut self, ident: &str, handle_css_module: bool) -> Result<(), PrinterError> { if handle_css_module { - if let Some(css_module) = &mut self.css_module { + if let Some(css_module) = &mut self.state.css_module { let dest = &mut self.dest; let mut first = true; css_module.config.pattern.write( - &css_module.hashes[self.loc.source_index as usize], - &css_module.sources[self.loc.source_index as usize], + &css_module.hashes[self.state.loc.source_index as usize], + &css_module.sources[self.state.loc.source_index as usize], ident, if let Some(content_hashes) = &css_module.content_hashes { - &content_hashes[self.loc.source_index as usize] + &content_hashes[self.state.loc.source_index as usize] } else { "" }, |s| { - self.col += s.len() as u32; + self.state.generated_col += s.len() as u32; if first { first = false; serialize_identifier(s, dest) @@ -312,7 +493,7 @@ impl<'a, 'c, W: std::fmt::Write + Sized> Printer<'a, 'c, W> { }, )?; - css_module.add_local(&ident, &ident, self.loc.source_index); + css_module.add_local(&ident, &ident, self.state.loc.source_index); return Ok(()); } } @@ -324,26 +505,26 @@ impl<'a, 'c, W: std::fmt::Write + Sized> Printer<'a, 'c, W> { pub(crate) fn write_dashed_ident(&mut self, ident: &str, is_declaration: bool) -> Result<(), PrinterError> { self.write_str("--")?; - match &mut self.css_module { + match &mut self.state.css_module { Some(css_module) if css_module.config.dashed_idents => { let dest = &mut self.dest; css_module.config.pattern.write( - &css_module.hashes[self.loc.source_index as usize], - &css_module.sources[self.loc.source_index as usize], + &css_module.hashes[self.state.loc.source_index as usize], + &css_module.sources[self.state.loc.source_index as usize], &ident[2..], if let Some(content_hashes) = &css_module.content_hashes { - &content_hashes[self.loc.source_index as usize] + &content_hashes[self.state.loc.source_index as usize] } else { "" }, |s| { - self.col += s.len() as u32; + self.state.generated_col += s.len() as u32; serialize_name(s, dest) }, )?; if is_declaration { - css_module.add_dashed(ident, self.loc.source_index); + css_module.add_dashed(ident, self.state.loc.source_index); } } _ => { @@ -366,56 +547,162 @@ impl<'a, 'c, W: std::fmt::Write + Sized> Printer<'a, 'c, W> { } } - pub(crate) fn with_context) -> Result>( - &mut self, - selectors: &SelectorList, - f: F, - ) -> Result { - let parent = std::mem::take(&mut self.context); - let ctx = StyleContext { - selectors: unsafe { std::mem::transmute(selectors) }, - parent, - }; + pub(crate) fn take_dependencies(&mut self) -> Option> { + self.state.dependencies.take() + } - // I can't figure out what lifetime to use here to convince the compiler that - // the reference doesn't live beyond the function call. - self.context = Some(unsafe { std::mem::transmute(&ctx) }); - let res = f(self); - self.context = parent; - res + /// Adds a mapping to the source map, if any. + pub fn add_mapping(&mut self, loc: Location) { + self.state.loc = loc; + + #[cfg(feature = "custom_sourcemap")] + if let Some(map) = &mut self.source_map { + #[cfg_attr(not(feature = "sourcemap"), allow(unused_mut))] + let mut original = OriginalLocation { + original_line: loc.line, + original_column: loc.column - 1, + source: loc.source_index, + name: None, + }; + + // Remap using input source map if possible. + if let Some(Some(sm)) = self.source_maps.get_mut(loc.source_index as usize) { + let mut found_mapping = false; + if let Some(orig) = sm.find_closest_mapping(loc.line, loc.column - 1) { + let source_index = map.add_source(sm.get_source(orig.source).unwrap()); + let name = orig.name.map(|name| map.add_name(sm.get_name(name).unwrap())); + original.original_line = orig.original_line; + original.original_column = orig.original_column; + original.source = source_index; + original.name = name; + + let content = sm.get_source_content(orig.source).unwrap().to_owned(); + map.set_source_content(source_index, &content); + + found_mapping = true; + } + + if !found_mapping { + return; + } + } + + map.add_mapping(self.state.generated_line, self.state.generated_col, Some(original)) + } } +} - pub(crate) fn with_cleared_context) -> Result>( - &mut self, - f: F, - ) -> Result { - let parent = std::mem::take(&mut self.context); - let res = f(self); - self.context = parent; - res +impl<'a, 'c, W: std::fmt::Write + Sized, S: SourceMap> std::fmt::Write for Printer<'a, 'c, W, S> { + fn write_str(&mut self, s: &str) -> std::fmt::Result { + self.state.generated_col += s.len() as u32; + self.dest.write_str(s) } +} + +mod private { + pub trait Sealed {} +} - pub(crate) fn with_parent_context) -> Result>( - &mut self, - f: F, - ) -> Result { - let parent = std::mem::take(&mut self.context); - if let Some(parent) = parent { - self.context = parent.parent; +/// A printer abstraction used by CSS serialization implementations. +#[allow(missing_docs)] +pub trait PrinterTrait: std::fmt::Write + private::Sealed { + fn options(&self) -> &PrinterOptions<'_>; + fn state(&self) -> &PrinterState<'_, '_>; + fn state_mut(&mut self) -> &mut PrinterState<'_, '_>; + fn filename(&self) -> &str; + fn error(&self, kind: PrinterErrorKind, loc: crate::dependencies::Location) -> Error { + Error { + kind, + loc: Some(ErrorLocation { + filename: self.filename().into(), + line: loc.line - 1, + column: loc.column, + }), } - let res = f(self); - self.context = parent; - res } - pub(crate) fn context(&self) -> Option<&'a StyleContext<'a, 'c>> { - self.context.clone() - } + fn whitespace(&mut self) -> Result<(), PrinterError>; + fn delim(&mut self, c: char, ws_before: bool) -> Result<(), PrinterError>; + fn newline(&mut self) -> Result<(), PrinterError>; + fn write_str_with_newlines(&mut self, s: &str) -> Result<(), PrinterError>; + fn add_mapping(&mut self, loc: Location); + fn indent(&mut self); + fn dedent(&mut self); + fn indent_by(&mut self, amt: u8); + fn dedent_by(&mut self, amt: u8); + fn write_ident(&mut self, ident: &str, handle_css_module: bool) -> Result<(), PrinterError>; + fn write_dashed_ident(&mut self, ident: &str, is_declaration: bool) -> Result<(), PrinterError>; + + fn without_css_module(&mut self, f: impl FnOnce(&mut Self) -> T) -> T; } -impl<'a, 'b, 'c, W: std::fmt::Write + Sized> std::fmt::Write for Printer<'a, 'c, W> { - fn write_str(&mut self, s: &str) -> std::fmt::Result { - self.col += s.len() as u32; - self.dest.write_str(s) +impl<'a, 'c, W: std::fmt::Write + Sized, S: SourceMap> private::Sealed for Printer<'a, 'c, W, S> {} + +impl<'a, 'c, W: std::fmt::Write + Sized, S: SourceMap> PrinterTrait for Printer<'a, 'c, W, S> { + fn options(&self) -> &PrinterOptions<'_> { + unsafe { std::mem::transmute::<&PrinterOptions<'a>, &PrinterOptions<'_>>(&self.options) } + } + + fn state(&self) -> &PrinterState<'_, '_> { + unsafe { std::mem::transmute::<&PrinterState<'a, 'c>, &PrinterState<'_, '_>>(&self.state) } + } + + fn state_mut(&mut self) -> &mut PrinterState<'_, '_> { + unsafe { std::mem::transmute::<&mut PrinterState<'a, 'c>, &mut PrinterState<'_, '_>>(&mut self.state) } + } + + fn filename(&self) -> &str { + Printer::filename(self) + } + + fn whitespace(&mut self) -> Result<(), PrinterError> { + Printer::whitespace(self) + } + + fn delim(&mut self, c: char, ws_before: bool) -> Result<(), PrinterError> { + Printer::delim(self, c, ws_before) + } + + fn newline(&mut self) -> Result<(), PrinterError> { + Printer::newline(self) + } + + fn write_str_with_newlines(&mut self, s: &str) -> Result<(), PrinterError> { + Printer::write_str_with_newlines(self, s) + } + + fn add_mapping(&mut self, loc: Location) { + Printer::add_mapping(self, loc) + } + + fn indent(&mut self) { + Printer::indent(self) + } + + fn dedent(&mut self) { + Printer::dedent(self) + } + + fn indent_by(&mut self, amt: u8) { + Printer::indent_by(self, amt) + } + + fn dedent_by(&mut self, amt: u8) { + Printer::dedent_by(self, amt) + } + + fn write_ident(&mut self, ident: &str, handle_css_module: bool) -> Result<(), PrinterError> { + Printer::write_ident(self, ident, handle_css_module) + } + + fn write_dashed_ident(&mut self, ident: &str, is_declaration: bool) -> Result<(), PrinterError> { + Printer::write_dashed_ident(self, ident, is_declaration) + } + + fn without_css_module(&mut self, f: impl FnOnce(&mut Self) -> T) -> T { + let css_module = self.state.css_module.take(); + let res = f(self); + self.state.css_module = css_module; + res } } diff --git a/src/properties/align.rs b/src/properties/align.rs index 4d0abd02d..35f365f0f 100644 --- a/src/properties/align.rs +++ b/src/properties/align.rs @@ -8,7 +8,6 @@ use crate::declaration::{DeclarationBlock, DeclarationList}; use crate::error::{ParserError, PrinterError}; use crate::macros::*; use crate::prefixes::{is_flex_2009, Feature}; -use crate::printer::Printer; use crate::traits::{FromStandard, Parse, PropertyHandler, Shorthand, ToCss}; use crate::values::length::LengthPercentage; use crate::values::number::NonNegative; @@ -59,13 +58,16 @@ impl<'i> Parse<'i> for BaselinePosition { } impl ToCss for BaselinePosition { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { - BaselinePosition::First => dest.write_str("baseline"), - BaselinePosition::Last => dest.write_str("last baseline"), + BaselinePosition::First => { + dest.write_str("baseline")?; + Ok(()) + } + BaselinePosition::Last => { + dest.write_str("last baseline")?; + Ok(()) + } } } } @@ -206,12 +208,12 @@ impl<'i> Parse<'i> for JustifyContent { } impl ToCss for JustifyContent { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { - JustifyContent::Normal => dest.write_str("normal"), + JustifyContent::Normal => { + dest.write_str("normal")?; + Ok(()) + } JustifyContent::ContentDistribution(value) => value.to_css(dest), JustifyContent::ContentPosition { overflow, value } => { if let Some(overflow) = overflow { @@ -227,7 +229,9 @@ impl ToCss for JustifyContent { dest.write_str(" ")?; } - dest.write_str("left") + dest.write_str("left")?; + + Ok(()) } JustifyContent::Right { overflow } => { if let Some(overflow) = overflow { @@ -235,7 +239,9 @@ impl ToCss for JustifyContent { dest.write_str(" ")?; } - dest.write_str("right") + dest.write_str("right")?; + + Ok(()) } } } @@ -280,10 +286,7 @@ impl<'i> Parse<'i> for PlaceContent { } impl ToCss for PlaceContent { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { self.align.to_css(dest)?; let is_equal = match self.justify { JustifyContent::Normal if self.align == AlignContent::Normal => true, @@ -429,14 +432,20 @@ impl<'i> Parse<'i> for JustifySelf { } impl ToCss for JustifySelf { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { - JustifySelf::Auto => dest.write_str("auto"), - JustifySelf::Normal => dest.write_str("normal"), - JustifySelf::Stretch => dest.write_str("stretch"), + JustifySelf::Auto => { + dest.write_str("auto")?; + Ok(()) + } + JustifySelf::Normal => { + dest.write_str("normal")?; + Ok(()) + } + JustifySelf::Stretch => { + dest.write_str("stretch")?; + Ok(()) + } JustifySelf::BaselinePosition(val) => val.to_css(dest), JustifySelf::SelfPosition { overflow, value } => { if let Some(overflow) = overflow { @@ -452,7 +461,9 @@ impl ToCss for JustifySelf { dest.write_str(" ")?; } - dest.write_str("left") + dest.write_str("left")?; + + Ok(()) } JustifySelf::Right { overflow } => { if let Some(overflow) = overflow { @@ -460,7 +471,9 @@ impl ToCss for JustifySelf { dest.write_str(" ")?; } - dest.write_str("right") + dest.write_str("right")?; + + Ok(()) } } } @@ -501,10 +514,7 @@ impl<'i> Parse<'i> for PlaceSelf { } impl ToCss for PlaceSelf { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { self.align.to_css(dest)?; let is_equal = match &self.justify { JustifySelf::Auto => true, @@ -610,15 +620,21 @@ impl<'i> Parse<'i> for LegacyJustify { } impl ToCss for LegacyJustify { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { dest.write_str("legacy ")?; match self { - LegacyJustify::Left => dest.write_str("left"), - LegacyJustify::Right => dest.write_str("right"), - LegacyJustify::Center => dest.write_str("center"), + LegacyJustify::Left => { + dest.write_str("left")?; + Ok(()) + } + LegacyJustify::Right => { + dest.write_str("right")?; + Ok(()) + } + LegacyJustify::Center => { + dest.write_str("center")?; + Ok(()) + } } } } @@ -699,13 +715,16 @@ impl<'i> Parse<'i> for JustifyItems { } impl ToCss for JustifyItems { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { - JustifyItems::Normal => dest.write_str("normal"), - JustifyItems::Stretch => dest.write_str("stretch"), + JustifyItems::Normal => { + dest.write_str("normal")?; + Ok(()) + } + JustifyItems::Stretch => { + dest.write_str("stretch")?; + Ok(()) + } JustifyItems::BaselinePosition(val) => val.to_css(dest), JustifyItems::Legacy(val) => val.to_css(dest), JustifyItems::SelfPosition { overflow, value } => { @@ -722,7 +741,9 @@ impl ToCss for JustifyItems { dest.write_str(" ")?; } - dest.write_str("left") + dest.write_str("left")?; + + Ok(()) } JustifyItems::Right { overflow } => { if let Some(overflow) = overflow { @@ -730,7 +751,9 @@ impl ToCss for JustifyItems { dest.write_str(" ")?; } - dest.write_str("right") + dest.write_str("right")?; + + Ok(()) } } } @@ -770,10 +793,7 @@ impl<'i> Parse<'i> for PlaceItems { } impl ToCss for PlaceItems { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { self.align.to_css(dest)?; let is_equal = match &self.justify { JustifyItems::Normal => self.align == AlignItems::Normal, @@ -833,10 +853,7 @@ impl<'i> Parse<'i> for Gap { } impl ToCss for Gap { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { self.row.to_css(dest)?; if self.column != self.row { dest.write_str(" ")?; diff --git a/src/properties/animation.rs b/src/properties/animation.rs index a488bbac1..956569f21 100644 --- a/src/properties/animation.rs +++ b/src/properties/animation.rs @@ -7,7 +7,6 @@ use crate::declaration::{DeclarationBlock, DeclarationList}; use crate::error::{ParserError, PrinterError}; use crate::macros::*; use crate::prefixes::Feature; -use crate::printer::Printer; use crate::properties::{Property, PropertyId, TokenOrValue, VendorPrefix}; use crate::traits::{Parse, PropertyHandler, Shorthand, ToCss, Zero}; use crate::values::ident::DashedIdent; @@ -46,27 +45,31 @@ pub enum AnimationName<'i> { } impl<'i> ToCss for AnimationName<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - let css_module_animation_enabled = - dest.css_module.as_ref().map_or(false, |css_module| css_module.config.animation); + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { + let css_module_animation_enabled = dest + .state() + .css_module + .as_ref() + .map_or(false, |css_module| css_module.config.animation); + let source_index = dest.state().loc.source_index; match self { - AnimationName::None => dest.write_str("none"), + AnimationName::None => { + dest.write_str("none")?; + Ok(()) + } AnimationName::Ident(s) => { if css_module_animation_enabled { - if let Some(css_module) = &mut dest.css_module { - css_module.reference(&s.0, dest.loc.source_index) + if let Some(css_module) = dest.state_mut().css_module.as_mut() { + css_module.reference(&s.0, source_index) } } s.to_css_with_options(dest, css_module_animation_enabled) } AnimationName::String(s) => { if css_module_animation_enabled { - if let Some(css_module) = &mut dest.css_module { - css_module.reference(&s, dest.loc.source_index) + if let Some(css_module) = dest.state_mut().css_module.as_mut() { + css_module.reference(&s, source_index) } } @@ -251,10 +254,7 @@ impl<'i> Parse<'i> for ScrollTimeline { } impl ToCss for ScrollTimeline { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { dest.write_str("scroll(")?; let mut needs_space = false; @@ -270,7 +270,9 @@ impl ToCss for ScrollTimeline { self.axis.to_css(dest)?; } - dest.write_char(')') + dest.write_char(')')?; + + Ok(()) } } @@ -354,10 +356,7 @@ impl<'i> Parse<'i> for ViewTimeline { } impl ToCss for ViewTimeline { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { dest.write_str("view(")?; let mut needs_space = false; if self.axis != ScrollAxis::default() { @@ -372,7 +371,9 @@ impl ToCss for ViewTimeline { self.inset.to_css(dest)?; } - dest.write_char(')') + dest.write_char(')')?; + + Ok(()) } } @@ -446,12 +447,16 @@ impl<'i> AnimationAttachmentRange { Ok(AnimationAttachmentRange::TimelineRange { name, offset }) } - fn to_css(&self, dest: &mut Printer, default: f32) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css( + &self, + dest: &mut PrinterT, + default: f32, + ) -> Result<(), PrinterError> { match self { - Self::Normal => dest.write_str("normal"), + Self::Normal => { + dest.write_str("normal")?; + Ok(()) + } Self::LengthPercentage(val) => val.to_css(dest), Self::TimelineRange { name, offset } => { name.to_css(dest)?; @@ -487,10 +492,7 @@ impl<'i> Parse<'i> for AnimationRangeStart { } impl ToCss for AnimationRangeStart { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { self.0.to_css(dest, 0.0) } } @@ -511,10 +513,7 @@ impl<'i> Parse<'i> for AnimationRangeEnd { } impl ToCss for AnimationRangeEnd { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { self.0.to_css(dest, 1.0) } } @@ -556,10 +555,7 @@ impl<'i> Parse<'i> for AnimationRange { } impl ToCss for AnimationRange { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { self.start.to_css(dest)?; let omit_end = match (&self.start.0, &self.end.0) { @@ -660,10 +656,7 @@ impl<'i> Parse<'i> for Animation<'i> { } impl<'i> ToCss for Animation<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match &self.name { AnimationName::None => {} AnimationName::Ident(CustomIdent(name)) | AnimationName::String(CSSString(name)) => { diff --git a/src/properties/background.rs b/src/properties/background.rs index 225d8c9dd..b95a2f37a 100644 --- a/src/properties/background.rs +++ b/src/properties/background.rs @@ -5,7 +5,6 @@ use crate::declaration::{DeclarationBlock, DeclarationList}; use crate::error::{ParserError, PrinterError}; use crate::macros::*; use crate::prefixes::Feature; -use crate::printer::Printer; use crate::properties::{Property, PropertyId, VendorPrefix}; use crate::targets::{Browsers, Targets}; use crate::traits::{FallbackValues, IsCompatible, Parse, PropertyHandler, Shorthand, ToCss}; @@ -74,15 +73,18 @@ impl<'i> Parse<'i> for BackgroundSize { } impl ToCss for BackgroundSize { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { use BackgroundSize::*; match &self { - Cover => dest.write_str("cover"), - Contain => dest.write_str("contain"), + Cover => { + dest.write_str("cover")?; + Ok(()) + } + Contain => { + dest.write_str("contain")?; + Ok(()) + } Explicit { width, height } => { width.to_css(dest)?; if *height != NonNegative(LengthPercentageOrAuto::Auto) { @@ -167,14 +169,17 @@ impl<'i> Parse<'i> for BackgroundRepeat { } impl ToCss for BackgroundRepeat { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { use BackgroundRepeatKeyword::*; match (&self.x, &self.y) { - (Repeat, NoRepeat) => dest.write_str("repeat-x"), - (NoRepeat, Repeat) => dest.write_str("repeat-y"), + (Repeat, NoRepeat) => { + dest.write_str("repeat-x")?; + Ok(()) + } + (NoRepeat, Repeat) => { + dest.write_str("repeat-y")?; + Ok(()) + } (x, y) => { x.to_css(dest)?; if y != x { @@ -314,10 +319,7 @@ impl<'i> Parse<'i> for BackgroundPosition { } impl ToCss for BackgroundPosition { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { let pos: Position = self.into(); pos.to_css(dest) } @@ -442,10 +444,7 @@ impl<'i> Parse<'i> for Background<'i> { } impl<'i> ToCss for Background<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { let mut has_output = false; if self.color != CssColor::default() { @@ -516,7 +515,7 @@ impl<'i> ToCss for Background<'i> { // If nothing was output, then this is the initial value, e.g. background: transparent if !has_output { - if dest.minify { + if dest.options().minify { // `0 0` is the shortest valid background value self.position.to_css(dest)?; } else { diff --git a/src/properties/border.rs b/src/properties/border.rs index 27aeba21e..a8a882bcf 100644 --- a/src/properties/border.rs +++ b/src/properties/border.rs @@ -8,7 +8,6 @@ use crate::declaration::{DeclarationBlock, DeclarationList}; use crate::error::{ParserError, PrinterError}; use crate::logical::PropertyCategory; use crate::macros::*; -use crate::printer::Printer; use crate::properties::custom::UnparsedProperty; use crate::properties::{Property, PropertyId}; use crate::targets::Browsers; @@ -179,10 +178,7 @@ impl<'i, S: Parse<'i> + Default, const P: u8> Parse<'i> for GenericBorder } impl ToCss for GenericBorder { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { if *self == Self::default() { self.style.to_css(dest)?; return Ok(()); diff --git a/src/properties/border_image.rs b/src/properties/border_image.rs index 54e39a297..84f5d3330 100644 --- a/src/properties/border_image.rs +++ b/src/properties/border_image.rs @@ -4,7 +4,6 @@ use crate::context::PropertyHandlerContext; use crate::declaration::{DeclarationBlock, DeclarationList}; use crate::error::{ParserError, PrinterError}; use crate::prefixes::Feature; -use crate::printer::Printer; use crate::properties::{Property, PropertyId, VendorPrefix}; use crate::targets::{Browsers, Targets}; use crate::traits::{IsCompatible, Parse, PropertyHandler, Shorthand, ToCss}; @@ -83,10 +82,7 @@ impl<'i> Parse<'i> for BorderImageRepeat { } impl ToCss for BorderImageRepeat { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { self.horizontal.to_css(dest)?; if self.horizontal != self.vertical { dest.write_str(" ")?; @@ -170,10 +166,7 @@ impl<'i> Parse<'i> for BorderImageSlice { } impl ToCss for BorderImageSlice { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { self.offsets.to_css(dest)?; if self.fill { dest.write_str(" fill")?; @@ -292,17 +285,14 @@ impl<'i> BorderImage<'i> { } } - pub(crate) fn to_css_internal( + pub(crate) fn to_css_internal( source: &Image<'i>, slice: &BorderImageSlice, width: &Rect, outset: &Rect>, repeat: &BorderImageRepeat, - dest: &mut Printer, - ) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + dest: &mut PrinterT, + ) -> Result<(), PrinterError> { if *source != Image::default() { source.to_css(dest)?; } @@ -335,10 +325,7 @@ impl<'i> BorderImage<'i> { } impl<'i> ToCss for BorderImage<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { BorderImage::to_css_internal(&self.source, &self.slice, &self.width, &self.outset, &self.repeat, dest) } } diff --git a/src/properties/border_radius.rs b/src/properties/border_radius.rs index 38097de04..bb870b3d6 100644 --- a/src/properties/border_radius.rs +++ b/src/properties/border_radius.rs @@ -7,7 +7,6 @@ use crate::error::{ParserError, PrinterError}; use crate::logical::PropertyCategory; use crate::macros::define_shorthand; use crate::prefixes::Feature; -use crate::printer::Printer; use crate::properties::{Property, PropertyId, VendorPrefix}; use crate::traits::{IsCompatible, Parse, PropertyHandler, Shorthand, ToCss, Zero}; use crate::values::length::*; @@ -66,10 +65,7 @@ impl<'i> Parse<'i> for BorderRadius { } impl ToCss for BorderRadius { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { let widths = Rect::new( &self.top_left.0, &self.top_right.0, diff --git a/src/properties/box_shadow.rs b/src/properties/box_shadow.rs index 35ea8c610..60f80209a 100644 --- a/src/properties/box_shadow.rs +++ b/src/properties/box_shadow.rs @@ -5,7 +5,6 @@ use crate::context::PropertyHandlerContext; use crate::declaration::DeclarationList; use crate::error::{ParserError, PrinterError}; use crate::prefixes::Feature; -use crate::printer::Printer; use crate::properties::Property; use crate::targets::Browsers; use crate::traits::{IsCompatible, Parse, PropertyHandler, ToCss, Zero}; @@ -100,10 +99,7 @@ impl<'i> Parse<'i> for BoxShadow { } impl ToCss for BoxShadow { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { if self.inset { dest.write_str("inset ")?; } diff --git a/src/properties/contain.rs b/src/properties/contain.rs index 27ad2bb95..2ae4b197d 100644 --- a/src/properties/contain.rs +++ b/src/properties/contain.rs @@ -12,7 +12,6 @@ use crate::{ declaration::{DeclarationBlock, DeclarationList}, error::{ParserError, PrinterError}, macros::{define_shorthand, enum_property, shorthand_handler}, - printer::Printer, properties::{Property, PropertyId}, rules::container::ContainerName as ContainerIdent, targets::Browsers, @@ -91,12 +90,12 @@ impl<'i> Parse<'i> for ContainerNameList<'i> { } impl<'i> ToCss for ContainerNameList<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { - ContainerNameList::None => dest.write_str("none"), + ContainerNameList::None => { + dest.write_str("none")?; + Ok(()) + } ContainerNameList::Names(names) => { let mut first = true; for name in names { @@ -143,10 +142,7 @@ impl<'i> Parse<'i> for Container<'i> { } impl<'i> ToCss for Container<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { self.name.to_css(dest)?; if self.container_type != ContainerType::default() { dest.delim('/', true)?; diff --git a/src/properties/css_modules.rs b/src/properties/css_modules.rs index dc8046f3e..3d497b452 100644 --- a/src/properties/css_modules.rs +++ b/src/properties/css_modules.rs @@ -2,7 +2,6 @@ use crate::dependencies::Location; use crate::error::{ParserError, PrinterError}; -use crate::printer::Printer; use crate::traits::{Parse, ToCss}; use crate::values::ident::{CustomIdent, CustomIdentList}; use crate::values::string::CowArcStr; @@ -87,10 +86,7 @@ fn parse_one_ident<'i, 't>( } impl ToCss for Composes<'_> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { let mut first = true; for name in &self.names { if first { @@ -122,10 +118,7 @@ impl<'i> Parse<'i> for Specifier<'i> { } impl<'i> ToCss for Specifier<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { Specifier::Global => dest.write_str("global")?, Specifier::File(file) => serialize_string(&file, dest)?, diff --git a/src/properties/custom.rs b/src/properties/custom.rs index 5b137a909..3fee0bf6f 100644 --- a/src/properties/custom.rs +++ b/src/properties/custom.rs @@ -3,6 +3,7 @@ use crate::error::{ParserError, PrinterError, PrinterErrorKind}; use crate::macros::enum_property; use crate::prefixes::Feature; +#[cfg(feature = "substitute_variables")] use crate::printer::Printer; use crate::properties::PropertyId; use crate::rules::supports::SupportsCondition; @@ -100,10 +101,7 @@ impl<'i> AsRef for CustomPropertyName<'i> { } impl<'i> ToCss for CustomPropertyName<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { CustomPropertyName::Custom(c) => c.to_css(dest), CustomPropertyName::Unknown(u) => u.to_css(dest), @@ -485,10 +483,11 @@ fn try_parse_color_token<'i, 't>( } impl<'i> TokenList<'i> { - pub(crate) fn to_css(&self, dest: &mut Printer, is_custom_property: bool) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + pub(crate) fn to_css( + &self, + dest: &mut PrinterT, + is_custom_property: bool, + ) -> Result<(), PrinterError> { for token_or_value in self.0.iter() { match token_or_value { TokenOrValue::Color(color) => { @@ -498,7 +497,7 @@ impl<'i> TokenList<'i> { color.to_css(dest, is_custom_property)?; } TokenOrValue::Url(url) => { - if dest.dependencies.is_some() && is_custom_property && !url.is_absolute() { + if dest.state().dependencies.is_some() && is_custom_property && !url.is_absolute() { return Err(dest.error( PrinterErrorKind::AmbiguousUrlInCustomProperty { url: url.url.as_ref().to_owned(), @@ -554,10 +553,10 @@ impl<'i> TokenList<'i> { Ok(()) } - pub(crate) fn to_css_raw(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + pub(crate) fn to_css_raw( + &self, + dest: &mut PrinterT, + ) -> Result<(), PrinterError> { for token_or_value in &self.0 { match token_or_value { TokenOrValue::Token(token) => { @@ -840,10 +839,7 @@ impl<'a> From<&cssparser::Token<'a>> for Token<'a> { impl<'a> ToCss for Token<'a> { #[inline] - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { use cssparser::ToCss; match self { Token::Ident(x) => cssparser::Token::Ident(x.as_ref().into()).to_css(dest)?, @@ -889,14 +885,14 @@ impl<'a> ToCss for Token<'a> { } .to_css(dest)?, Token::WhiteSpace(w) => { - if dest.minify { + if dest.options().minify { dest.write_char(' ')?; } else { dest.write_str(&w)?; } } Token::Comment(c) => { - if !dest.minify { + if !dest.options().minify { cssparser::Token::Comment(c).to_css(dest)?; } } @@ -1208,10 +1204,11 @@ impl<'i> Variable<'i> { Ok(Variable { name, fallback }) } - fn to_css(&self, dest: &mut Printer, is_custom_property: bool) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css( + &self, + dest: &mut PrinterT, + is_custom_property: bool, + ) -> Result<(), PrinterError> { dest.write_str("var(")?; self.name.to_css(dest)?; if let Some(fallback) = &self.fallback { @@ -1221,7 +1218,8 @@ impl<'i> Variable<'i> { } fallback.to_css(dest, is_custom_property)?; } - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } fn get_fallback(&self, kind: ColorFallbackKind) -> Self { @@ -1333,10 +1331,7 @@ impl<'i> Parse<'i> for EnvironmentVariableName<'i> { } impl<'i> ToCss for EnvironmentVariableName<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { EnvironmentVariableName::UA(ua) => ua.to_css(dest), EnvironmentVariableName::Custom(custom) => custom.to_css(dest), @@ -1379,10 +1374,11 @@ impl<'i> EnvironmentVariable<'i> { }) } - pub(crate) fn to_css(&self, dest: &mut Printer, is_custom_property: bool) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + pub(crate) fn to_css( + &self, + dest: &mut PrinterT, + is_custom_property: bool, + ) -> Result<(), PrinterError> { dest.write_str("env(")?; self.name.to_css(dest)?; @@ -1398,7 +1394,8 @@ impl<'i> EnvironmentVariable<'i> { } fallback.to_css(dest, is_custom_property)?; } - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } fn get_fallback(&self, kind: ColorFallbackKind) -> Self { @@ -1426,14 +1423,16 @@ pub struct Function<'i> { } impl<'i> Function<'i> { - fn to_css(&self, dest: &mut Printer, is_custom_property: bool) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css( + &self, + dest: &mut PrinterT, + is_custom_property: bool, + ) -> Result<(), PrinterError> { self.name.to_css(dest)?; dest.write_char('(')?; self.arguments.to_css(dest, is_custom_property)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } fn get_fallback(&self, kind: ColorFallbackKind) -> Self { @@ -1550,13 +1549,14 @@ impl<'i> UnresolvedColor<'i> { } } - fn to_css(&self, dest: &mut Printer, is_custom_property: bool) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css( + &self, + dest: &mut PrinterT, + is_custom_property: bool, + ) -> Result<(), PrinterError> { match self { UnresolvedColor::RGB { r, g, b, alpha } => { - if should_compile!(dest.targets.current, SpaceSeparatedColorNotation) { + if should_compile!(dest.state().targets.current, SpaceSeparatedColorNotation) { dest.write_str("rgba(")?; r.to_css(dest)?; dest.delim(',', false)?; @@ -1577,10 +1577,11 @@ impl<'i> UnresolvedColor<'i> { b.to_css(dest)?; dest.delim('/', true)?; alpha.to_css(dest, is_custom_property)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } UnresolvedColor::HSL { h, s, l, alpha } => { - if should_compile!(dest.targets.current, SpaceSeparatedColorNotation) { + if should_compile!(dest.state().targets.current, SpaceSeparatedColorNotation) { dest.write_str("hsla(")?; h.to_css(dest)?; dest.delim(',', false)?; @@ -1601,10 +1602,11 @@ impl<'i> UnresolvedColor<'i> { Percentage(*l / 100.0).to_css(dest)?; dest.delim('/', true)?; alpha.to_css(dest, is_custom_property)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } UnresolvedColor::LightDark { light, dark } => { - if should_compile!(dest.targets.current, LightDark) { + if should_compile!(dest.state().targets.current, LightDark) { dest.write_str("var(--lightningcss-light")?; dest.delim(',', false)?; light.to_css(dest, is_custom_property)?; @@ -1613,14 +1615,15 @@ impl<'i> UnresolvedColor<'i> { dest.write_str("var(--lightningcss-dark")?; dest.delim(',', false)?; dark.to_css(dest, is_custom_property)?; - return dest.write_char(')'); + return Ok(dest.write_char(')')?); } dest.write_str("light-dark(")?; light.to_css(dest, is_custom_property)?; dest.delim(',', false)?; dark.to_css(dest, is_custom_property)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } } } diff --git a/src/properties/display.rs b/src/properties/display.rs index 3df7c8193..1c1fdf86a 100644 --- a/src/properties/display.rs +++ b/src/properties/display.rs @@ -7,7 +7,6 @@ use crate::declaration::DeclarationList; use crate::error::{ParserError, PrinterError}; use crate::macros::enum_property; use crate::prefixes::{is_flex_2009, Feature}; -use crate::printer::Printer; use crate::traits::{Parse, PropertyHandler, ToCss}; use crate::vendor_prefix::VendorPrefix; #[cfg(feature = "visitor")] @@ -67,28 +66,43 @@ impl<'i> Parse<'i> for DisplayInside { } impl ToCss for DisplayInside { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { - DisplayInside::Flow => dest.write_str("flow"), - DisplayInside::FlowRoot => dest.write_str("flow-root"), - DisplayInside::Table => dest.write_str("table"), + DisplayInside::Flow => { + dest.write_str("flow")?; + Ok(()) + } + DisplayInside::FlowRoot => { + dest.write_str("flow-root")?; + Ok(()) + } + DisplayInside::Table => { + dest.write_str("table")?; + Ok(()) + } DisplayInside::Flex(prefix) => { prefix.to_css(dest)?; if *prefix == VendorPrefix::Ms { - dest.write_str("flexbox") + dest.write_str("flexbox")?; + Ok(()) } else { - dest.write_str("flex") + dest.write_str("flex")?; + Ok(()) } } DisplayInside::Box(prefix) => { prefix.to_css(dest)?; - dest.write_str("box") + dest.write_str("box")?; + Ok(()) + } + DisplayInside::Grid => { + dest.write_str("grid")?; + Ok(()) + } + DisplayInside::Ruby => { + dest.write_str("ruby")?; + Ok(()) } - DisplayInside::Grid => dest.write_str("grid"), - DisplayInside::Ruby => dest.write_str("ruby"), } } } @@ -226,21 +240,24 @@ impl<'i> Parse<'i> for DisplayPair { } impl ToCss for DisplayPair { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { DisplayPair { outside: DisplayOutside::Inline, inside: DisplayInside::FlowRoot, is_list_item: false, - } => dest.write_str("inline-block"), + } => { + dest.write_str("inline-block")?; + Ok(()) + } DisplayPair { outside: DisplayOutside::Inline, inside: DisplayInside::Table, is_list_item: false, - } => dest.write_str("inline-table"), + } => { + dest.write_str("inline-table")?; + Ok(()) + } DisplayPair { outside: DisplayOutside::Inline, inside: DisplayInside::Flex(prefix), @@ -248,9 +265,11 @@ impl ToCss for DisplayPair { } => { prefix.to_css(dest)?; if *prefix == VendorPrefix::Ms { - dest.write_str("inline-flexbox") + dest.write_str("inline-flexbox")?; + Ok(()) } else { - dest.write_str("inline-flex") + dest.write_str("inline-flex")?; + Ok(()) } } DisplayPair { @@ -259,13 +278,17 @@ impl ToCss for DisplayPair { is_list_item: false, } => { prefix.to_css(dest)?; - dest.write_str("inline-box") + dest.write_str("inline-box")?; + Ok(()) } DisplayPair { outside: DisplayOutside::Inline, inside: DisplayInside::Grid, is_list_item: false, - } => dest.write_str("inline-grid"), + } => { + dest.write_str("inline-grid")?; + Ok(()) + } DisplayPair { outside, inside, diff --git a/src/properties/effects.rs b/src/properties/effects.rs index 78c9baea9..b93564431 100644 --- a/src/properties/effects.rs +++ b/src/properties/effects.rs @@ -2,7 +2,6 @@ use crate::error::{ParserError, PrinterError}; use crate::macros::enum_property; -use crate::printer::Printer; use crate::targets::{Browsers, Targets}; use crate::traits::{FallbackValues, IsCompatible, Parse, ToCss, Zero}; use crate::values::color::ColorFallbackKind; @@ -117,17 +116,15 @@ impl<'i> Parse<'i> for Filter<'i> { } impl<'i> ToCss for Filter<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { Filter::Blur(val) => { dest.write_str("blur(")?; if *val != NonNegative::zero() { val.to_css(dest)?; } - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } Filter::Brightness(val) => { dest.write_str("brightness(")?; @@ -135,7 +132,8 @@ impl<'i> ToCss for Filter<'i> { if v != 1.0 { val.to_css(dest)?; } - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } Filter::Contrast(val) => { dest.write_str("contrast(")?; @@ -143,7 +141,8 @@ impl<'i> ToCss for Filter<'i> { if v != 1.0 { val.to_css(dest)?; } - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } Filter::Grayscale(val) => { dest.write_str("grayscale(")?; @@ -151,14 +150,16 @@ impl<'i> ToCss for Filter<'i> { if v != 1.0 { val.to_css(dest)?; } - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } Filter::HueRotate(val) => { dest.write_str("hue-rotate(")?; if !val.is_zero() { val.to_css(dest)?; } - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } Filter::Invert(val) => { dest.write_str("invert(")?; @@ -166,7 +167,8 @@ impl<'i> ToCss for Filter<'i> { if v != 1.0 { val.to_css(dest)?; } - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } Filter::Opacity(val) => { dest.write_str("opacity(")?; @@ -174,7 +176,8 @@ impl<'i> ToCss for Filter<'i> { if v != 1.0 { val.to_css(dest)?; } - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } Filter::Saturate(val) => { dest.write_str("saturate(")?; @@ -182,7 +185,8 @@ impl<'i> ToCss for Filter<'i> { if v != 1.0 { val.to_css(dest)?; } - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } Filter::Sepia(val) => { dest.write_str("sepia(")?; @@ -190,12 +194,14 @@ impl<'i> ToCss for Filter<'i> { if v != 1.0 { val.to_css(dest)?; } - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } Filter::DropShadow(val) => { dest.write_str("drop-shadow(")?; val.to_css(dest)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } Filter::Url(url) => url.to_css(dest), } @@ -279,10 +285,7 @@ impl<'i> Parse<'i> for DropShadow { } impl ToCss for DropShadow { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { self.x_offset.to_css(dest)?; dest.write_char(' ')?; self.y_offset.to_css(dest)?; @@ -345,12 +348,12 @@ impl<'i> Parse<'i> for FilterList<'i> { } impl<'i> ToCss for FilterList<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { - FilterList::None => dest.write_str("none"), + FilterList::None => { + dest.write_str("none")?; + Ok(()) + } FilterList::Filters(filters) => { let mut first = true; for filter in filters { diff --git a/src/properties/flex.rs b/src/properties/flex.rs index 0a474b222..430006aeb 100644 --- a/src/properties/flex.rs +++ b/src/properties/flex.rs @@ -9,7 +9,6 @@ use crate::declaration::{DeclarationBlock, DeclarationList}; use crate::error::{ParserError, PrinterError}; use crate::macros::*; use crate::prefixes::{is_flex_2009, Feature}; -use crate::printer::Printer; use crate::traits::{FromStandard, Parse, PropertyHandler, Shorthand, ToCss, Zero}; use crate::values::number::{CSSInteger, CSSNumber, NonNegative}; use crate::values::{ @@ -103,10 +102,7 @@ impl<'i> Parse<'i> for FlexFlow { } impl ToCss for FlexFlow { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { let mut needs_space = false; if self.direction != FlexDirection::default() || self.wrap == FlexWrap::default() { self.direction.to_css(dest)?; @@ -180,10 +176,7 @@ impl<'i> Parse<'i> for Flex { } impl ToCss for Flex { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { if self.grow.0 == 0.0 && self.shrink.0 == 0.0 && self.basis.0 == LengthPercentageOrAuto::Auto { dest.write_str("none")?; return Ok(()); diff --git a/src/properties/font.rs b/src/properties/font.rs index 10fa9b84c..e61d2b727 100644 --- a/src/properties/font.rs +++ b/src/properties/font.rs @@ -8,7 +8,6 @@ use crate::context::PropertyHandlerContext; use crate::declaration::{DeclarationBlock, DeclarationList}; use crate::error::{ParserError, PrinterError}; use crate::macros::*; -use crate::printer::Printer; use crate::targets::should_compile; use crate::traits::{IsCompatible, Parse, PropertyHandler, Shorthand, ToCss}; use crate::values::length::LengthValue; @@ -81,15 +80,18 @@ impl Default for AbsoluteFontWeight { } impl ToCss for AbsoluteFontWeight { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { use AbsoluteFontWeight::*; match self { Weight(val) => val.to_css(dest), - Normal => dest.write_str(if dest.minify { "400" } else { "normal" }), - Bold => dest.write_str(if dest.minify { "700" } else { "bold" }), + Normal => { + dest.write_str(if dest.options().minify { "400" } else { "normal" })?; + Ok(()) + } + Bold => { + dest.write_str(if dest.options().minify { "700" } else { "bold" })?; + Ok(()) + } } } } @@ -262,11 +264,8 @@ impl Into for &FontStretch { } impl ToCss for FontStretch { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - if dest.minify { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { + if dest.options().minify { let percentage: Percentage = self.into(); return percentage.to_css(dest); } @@ -363,10 +362,7 @@ impl<'i> Parse<'i> for FontFamily<'i> { } impl<'i> ToCss for FontFamily<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { FontFamily::Generic(val) => val.to_css(dest), FontFamily::FamilyName(val) => val.to_css(dest), @@ -412,10 +408,7 @@ impl<'i> Parse<'i> for FamilyName<'i> { } impl<'i> ToCss for FamilyName<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { // Generic family names such as sans-serif must be quoted if parsed as a string. // CSS wide keywords, as well as "default", must also be quoted. // https://www.w3.org/TR/css-fonts-4/#family-name-syntax @@ -439,7 +432,7 @@ impl<'i> ToCss for FamilyName<'i> { serialize_identifier(slice, &mut id)?; } if id.len() < val.len() + 2 { - return dest.write_str(&id); + return Ok(dest.write_str(&id)?); } } serialize_string(&val, dest)?; @@ -507,13 +500,16 @@ impl<'i> Parse<'i> for FontStyle { } impl ToCss for FontStyle { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { - FontStyle::Normal => dest.write_str("normal"), - FontStyle::Italic => dest.write_str("italic"), + FontStyle::Normal => { + dest.write_str("normal")?; + Ok(()) + } + FontStyle::Italic => { + dest.write_str("italic")?; + Ok(()) + } FontStyle::Oblique(angle) => { dest.write_str("oblique")?; if *angle != FontStyle::default_oblique_angle() { @@ -755,10 +751,7 @@ impl<'i> Parse<'i> for Font<'i> { } impl<'i> ToCss for Font<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { if self.style != FontStyle::default() { self.style.to_css(dest)?; dest.write_char(' ')?; diff --git a/src/properties/grid.rs b/src/properties/grid.rs index 82fe5fedd..efb70fe4b 100644 --- a/src/properties/grid.rs +++ b/src/properties/grid.rs @@ -6,7 +6,6 @@ use crate::context::PropertyHandlerContext; use crate::declaration::{DeclarationBlock, DeclarationList}; use crate::error::{Error, ErrorLocation, ParserError, PrinterError, PrinterErrorKind}; use crate::macros::{define_shorthand, impl_shorthand}; -use crate::printer::Printer; use crate::properties::{Property, PropertyId}; use crate::traits::{Parse, PropertyHandler, Shorthand, ToCss}; use crate::values::ident::CustomIdent; @@ -220,10 +219,7 @@ impl<'i> Parse<'i> for TrackSize { } impl ToCss for TrackSize { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { TrackSize::TrackBreadth(breadth) => breadth.to_css(dest), TrackSize::MinMax { min, max } => { @@ -231,12 +227,14 @@ impl ToCss for TrackSize { min.to_css(dest)?; dest.delim(',', false)?; max.to_css(dest)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } TrackSize::FitContent(len) => { dest.write_str("fit-content(")?; len.to_css(dest)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } } } @@ -289,14 +287,20 @@ impl TrackBreadth { } impl ToCss for TrackBreadth { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { - TrackBreadth::Auto => dest.write_str("auto"), - TrackBreadth::MinContent => dest.write_str("min-content"), - TrackBreadth::MaxContent => dest.write_str("max-content"), + TrackBreadth::Auto => { + dest.write_str("auto")?; + Ok(()) + } + TrackBreadth::MinContent => { + dest.write_str("min-content")?; + Ok(()) + } + TrackBreadth::MaxContent => { + dest.write_str("max-content")?; + Ok(()) + } TrackBreadth::Length(len) => len.to_css(dest), TrackBreadth::Flex(flex) => serialize_dimension(flex.0, "fr", dest), } @@ -335,10 +339,7 @@ impl<'i> Parse<'i> for TrackRepeat<'i> { } impl<'i> ToCss for TrackRepeat<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { dest.write_str("repeat(")?; self.count.to_css(dest)?; dest.delim(',', false)?; @@ -363,7 +364,9 @@ impl<'i> ToCss for TrackRepeat<'i> { first = false; } - dest.write_char(')') + dest.write_char(')')?; + + Ok(()) } } @@ -380,10 +383,10 @@ fn parse_line_names<'i, 't>( }) } -fn serialize_line_names(names: &[CustomIdent], dest: &mut Printer) -> Result<(), PrinterError> -where - W: std::fmt::Write, -{ +fn serialize_line_names( + names: &[CustomIdent], + dest: &mut PrinterT, +) -> Result<(), PrinterError> { dest.write_char('[')?; let mut first = true; for name in names { @@ -394,24 +397,26 @@ where } write_ident(&name.0, dest)?; } - dest.write_char(']') + dest.write_char(']')?; + Ok(()) } -fn write_ident(name: &str, dest: &mut Printer) -> Result<(), PrinterError> -where - W: std::fmt::Write, -{ - let css_module_grid_enabled = dest.css_module.as_ref().map_or(false, |css_module| css_module.config.grid); +fn write_ident(name: &str, dest: &mut PrinterT) -> Result<(), PrinterError> { + let css_module_grid_enabled = dest + .state() + .css_module + .as_ref() + .map_or(false, |css_module| css_module.config.grid); if css_module_grid_enabled { - if let Some(css_module) = &mut dest.css_module { + if let Some(css_module) = dest.state_mut().css_module.as_mut() { if let Some(last) = css_module.config.pattern.segments.last() { if !matches!(last, crate::css_modules::Segment::Local) { return Err(Error { kind: PrinterErrorKind::InvalidCssModulesPatternInGrid, loc: Some(ErrorLocation { filename: dest.filename().into(), - line: dest.loc.line, - column: dest.loc.column, + line: dest.state().loc.line, + column: dest.state().loc.column, }), }); } @@ -451,10 +456,7 @@ impl<'i> Parse<'i> for TrackList<'i> { } impl<'i> ToCss for TrackList<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { let mut items_iter = self.items.iter(); let line_names_iter = self.line_names.iter(); let mut first = true; @@ -513,12 +515,9 @@ impl<'i> Parse<'i> for TrackSizeList { } impl ToCss for TrackSizeList { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { if self.0.len() == 0 { - return dest.write_str("auto"); + return Ok(dest.write_str("auto")?); } let mut first = true; @@ -627,18 +626,18 @@ fn is_name_code_point(c: char) -> bool { } impl ToCss for GridTemplateAreas { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { - GridTemplateAreas::None => dest.write_str("none"), + GridTemplateAreas::None => { + dest.write_str("none")?; + Ok(()) + } GridTemplateAreas::Areas { areas, .. } => { let mut iter = areas.iter(); let mut next = iter.next(); let mut first = true; while next.is_some() { - if !first && !dest.minify { + if !first && !dest.options().minify { dest.newline()?; } @@ -646,14 +645,14 @@ impl ToCss for GridTemplateAreas { if first { first = false; - if !dest.minify { + if !dest.options().minify { // Indent by the width of "grid-template-areas: ", so the rows line up. dest.indent_by(21); } } } - if !dest.minify { + if !dest.options().minify { dest.dedent_by(21); } @@ -664,15 +663,12 @@ impl ToCss for GridTemplateAreas { } impl GridTemplateAreas { - fn write_string<'a, W>( + fn write_string<'a, PrinterT: crate::printer::PrinterTrait>( &self, - dest: &mut Printer, + dest: &mut PrinterT, iter: &mut std::slice::Iter<'a, Option>, next: &mut Option<&'a Option>, - ) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + ) -> Result<(), PrinterError> { let columns = match self { GridTemplateAreas::Areas { columns, .. } => *columns, _ => unreachable!(), @@ -684,13 +680,13 @@ impl GridTemplateAreas { for i in 0..columns { if let Some(token) = next { if let Some(string) = token { - if i > 0 && (!last_was_null || !dest.minify) { + if i > 0 && (!last_was_null || !dest.options().minify) { dest.write_char(' ')?; } write_ident(string, dest)?; last_was_null = false; } else { - if i > 0 && (last_was_null || !dest.minify) { + if i > 0 && (last_was_null || !dest.options().minify) { dest.write_char(' ')?; } dest.write_char('.')?; @@ -701,7 +697,9 @@ impl GridTemplateAreas { *next = iter.next(); } - dest.write_char('"') + dest.write_char('"')?; + + Ok(()) } } @@ -806,19 +804,17 @@ impl<'i> Parse<'i> for GridTemplate<'i> { } impl ToCss for GridTemplate<'_> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { self.to_css_with_indent(dest, 15) } } impl GridTemplate<'_> { - fn to_css_with_indent(&self, dest: &mut Printer, indent: u8) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css_with_indent( + &self, + dest: &mut PrinterT, + indent: u8, + ) -> Result<(), PrinterError> { match &self.areas { GridTemplateAreas::None => { if self.rows == TrackSizing::None && self.columns == TrackSizing::None { @@ -845,7 +841,7 @@ impl GridTemplate<'_> { while next.is_some() { macro_rules! newline { () => { - if !dest.minify { + if !dest.options().minify { if !indented { // Indent by the width of "grid-template: ", so the rows line up. dest.indent_by(indent); @@ -858,7 +854,7 @@ impl GridTemplate<'_> { if let Some(line_names) = line_names_iter.next() { if !line_names.is_empty() { - if !dest.minify && line_names.len() == 2 { + if !dest.options().minify && line_names.len() == 2 { dest.whitespace()?; serialize_line_names(&line_names[0..1], dest)?; newline!(); @@ -1082,16 +1078,13 @@ impl<'i> Parse<'i> for GridAutoFlow { } impl ToCss for GridAutoFlow { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { let s = if *self == GridAutoFlow::Row { "row" } else if *self == GridAutoFlow::Column { "column" } else if *self == GridAutoFlow::Row | GridAutoFlow::Dense { - if dest.minify { + if dest.options().minify { "dense" } else { "row dense" @@ -1102,7 +1095,9 @@ impl ToCss for GridAutoFlow { unreachable!(); }; - dest.write_str(s) + dest.write_str(s)?; + + Ok(()) } } @@ -1200,10 +1195,7 @@ fn parse_grid_auto_flow<'i, 't>( } impl ToCss for Grid<'_> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { let is_auto_initial = self.auto_rows == TrackSizeList::default() && self.auto_columns == TrackSizeList::default() && self.auto_flow == GridAutoFlow::default(); @@ -1431,12 +1423,12 @@ impl<'i> Parse<'i> for GridLine<'i> { } impl ToCss for GridLine<'_> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { - GridLine::Auto => dest.write_str("auto"), + GridLine::Auto => { + dest.write_str("auto")?; + Ok(()) + } GridLine::Area { name } => write_ident(&name.0, dest), GridLine::Line { index, name } => { index.to_css(dest)?; @@ -1500,10 +1492,7 @@ macro_rules! impl_grid_placement { } impl ToCss for $name<'_> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { self.start.to_css(dest)?; if !self.start.can_omit_end(&self.end) { @@ -1609,10 +1598,7 @@ impl<'i> Parse<'i> for GridArea<'i> { } impl ToCss for GridArea<'_> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { self.row_start.to_css(dest)?; let can_omit_column_end = self.column_start.can_omit_end(&self.column_end); diff --git a/src/properties/list.rs b/src/properties/list.rs index e04ed7477..cf43a589a 100644 --- a/src/properties/list.rs +++ b/src/properties/list.rs @@ -5,7 +5,6 @@ use crate::context::PropertyHandlerContext; use crate::declaration::{DeclarationBlock, DeclarationList}; use crate::error::{ParserError, PrinterError}; use crate::macros::{define_shorthand, enum_property, shorthand_handler}; -use crate::printer::Printer; use crate::targets::{Browsers, Targets}; use crate::traits::{FallbackValues, IsCompatible, Parse, PropertyHandler, Shorthand, ToCss}; use crate::values::string::CSSString; @@ -213,15 +212,13 @@ impl<'i> Parse<'i> for CounterStyle<'i> { } impl ToCss for CounterStyle<'_> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { CounterStyle::Predefined(style) => style.to_css(dest), CounterStyle::Name(name) => { - if let Some(css_module) = &mut dest.css_module { - css_module.reference(&name.0, dest.loc.source_index) + let source_index = dest.state().loc.source_index; + if let Some(css_module) = dest.state_mut().css_module.as_mut() { + css_module.reference(&name.0, source_index) } name.to_css(dest) } @@ -240,7 +237,8 @@ impl ToCss for CounterStyle<'_> { symbol.to_css(dest)?; needs_space = true; } - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } } } @@ -412,10 +410,7 @@ impl<'i> Parse<'i> for ListStyle<'i> { } impl<'i> ToCss for ListStyle<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { let mut needs_space = false; if self.position != ListStylePosition::default() { self.position.to_css(dest)?; diff --git a/src/properties/margin_padding.rs b/src/properties/margin_padding.rs index c467ed954..31f7feaf9 100644 --- a/src/properties/margin_padding.rs +++ b/src/properties/margin_padding.rs @@ -4,7 +4,6 @@ use crate::declaration::{DeclarationBlock, DeclarationList}; use crate::error::{ParserError, PrinterError}; use crate::logical::PropertyCategory; use crate::macros::{define_shorthand, rect_shorthand, size_shorthand}; -use crate::printer::Printer; use crate::properties::{Property, PropertyId}; use crate::traits::{IsCompatible, Parse, PropertyHandler, Shorthand, ToCss}; use crate::values::number::NonNegative; diff --git a/src/properties/masking.rs b/src/properties/masking.rs index 784e6b118..ba29cd0bb 100644 --- a/src/properties/masking.rs +++ b/src/properties/masking.rs @@ -8,7 +8,6 @@ use crate::declaration::{DeclarationBlock, DeclarationList}; use crate::error::{ParserError, PrinterError}; use crate::macros::{define_list_shorthand, define_shorthand, enum_property, property_bitflags}; use crate::prefixes::Feature; -use crate::printer::Printer; use crate::properties::Property; use crate::targets::{Browsers, Targets}; use crate::traits::{FallbackValues, IsCompatible, Parse, PropertyHandler, Shorthand, ToCss}; @@ -311,10 +310,7 @@ impl<'i> Parse<'i> for Mask<'i> { } impl<'i> ToCss for Mask<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { self.image.to_css(dest)?; if self.position != Position::default() || self.size != BackgroundSize::default() { @@ -428,12 +424,12 @@ impl<'i> Parse<'i> for ClipPath<'i> { } impl<'i> ToCss for ClipPath<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { - ClipPath::None => dest.write_str("none"), + ClipPath::None => { + dest.write_str("none")?; + Ok(()) + } ClipPath::Url(url) => url.to_css(dest), ClipPath::Shape { shape, @@ -517,10 +513,7 @@ impl<'i> Parse<'i> for MaskBorder<'i> { } impl<'i> ToCss for MaskBorder<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { BorderImage::to_css_internal(&self.source, &self.slice, &self.width, &self.outset, &self.repeat, dest)?; if self.mode != MaskBorderMode::default() { dest.write_char(' ')?; diff --git a/src/properties/mod.rs b/src/properties/mod.rs index 097f1f45f..d388c1037 100644 --- a/src/properties/mod.rs +++ b/src/properties/mod.rs @@ -245,7 +245,7 @@ macro_rules! define_properties { } impl<'i> ToCss for PropertyId<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> where W: std::fmt::Write { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { let mut first = true; macro_rules! delim { () => { @@ -772,7 +772,7 @@ macro_rules! define_properties { } /// Serializes the value of a CSS property without its name or `!important` flag. - pub fn value_to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> where W: std::fmt::Write { + pub fn value_to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { use Property::*; match self { @@ -801,7 +801,7 @@ macro_rules! define_properties { } /// Serializes the CSS property, with an optional `!important` flag. - pub fn to_css(&self, dest: &mut Printer, important: bool) -> Result<(), PrinterError> where W: std::fmt::Write { + pub fn to_css(&self, dest: &mut PrinterT, important: bool) -> Result<(), PrinterError> { use Property::*; let mut first = true; @@ -1642,10 +1642,7 @@ impl<'i, T: smallvec::Array, V: Parse<'i>> Parse<'i> for SmallVec { } impl, V: ToCss> ToCss for SmallVec { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { let len = self.len(); for (idx, val) in self.iter().enumerate() { val.to_css(dest)?; @@ -1664,10 +1661,7 @@ impl<'i, T: Parse<'i>> Parse<'i> for Vec { } impl ToCss for Vec { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { let len = self.len(); for (idx, val) in self.iter().enumerate() { val.to_css(dest)?; diff --git a/src/properties/outline.rs b/src/properties/outline.rs index 2ecdb2024..582a0c2ef 100644 --- a/src/properties/outline.rs +++ b/src/properties/outline.rs @@ -6,7 +6,6 @@ use crate::context::PropertyHandlerContext; use crate::declaration::{DeclarationBlock, DeclarationList}; use crate::error::{ParserError, PrinterError}; use crate::macros::{impl_shorthand, shorthand_handler}; -use crate::printer::Printer; use crate::targets::Browsers; use crate::traits::{FallbackValues, IsCompatible, Parse, PropertyHandler, Shorthand, ToCss}; use crate::values::color::CssColor; diff --git a/src/properties/overflow.rs b/src/properties/overflow.rs index bb5e54b09..6ccfb7206 100644 --- a/src/properties/overflow.rs +++ b/src/properties/overflow.rs @@ -6,7 +6,6 @@ use crate::context::PropertyHandlerContext; use crate::declaration::{DeclarationBlock, DeclarationList}; use crate::error::{ParserError, PrinterError}; use crate::macros::{define_shorthand, enum_property}; -use crate::printer::Printer; use crate::traits::{Parse, PropertyHandler, Shorthand, ToCss}; #[cfg(feature = "visitor")] use crate::visitor::Visit; @@ -48,10 +47,7 @@ impl<'i> Parse<'i> for Overflow { } impl ToCss for Overflow { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { self.x.to_css(dest)?; if self.y != self.x { dest.write_char(' ')?; diff --git a/src/properties/position.rs b/src/properties/position.rs index 34a0cf3bf..4496028fa 100644 --- a/src/properties/position.rs +++ b/src/properties/position.rs @@ -5,7 +5,6 @@ use crate::context::PropertyHandlerContext; use crate::declaration::DeclarationList; use crate::error::{ParserError, PrinterError}; use crate::prefixes::Feature; -use crate::printer::Printer; use crate::traits::{Parse, PropertyHandler, ToCss}; use crate::values::number::CSSInteger; use crate::vendor_prefix::VendorPrefix; @@ -55,18 +54,28 @@ impl<'i> Parse<'i> for Position { } impl ToCss for Position { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { - Position::Static => dest.write_str("static"), - Position::Relative => dest.write_str("relative"), - Position::Absolute => dest.write_str("absolute"), - Position::Fixed => dest.write_str("fixed"), + Position::Static => { + dest.write_str("static")?; + Ok(()) + } + Position::Relative => { + dest.write_str("relative")?; + Ok(()) + } + Position::Absolute => { + dest.write_str("absolute")?; + Ok(()) + } + Position::Fixed => { + dest.write_str("fixed")?; + Ok(()) + } Position::Sticky(prefix) => { prefix.to_css(dest)?; - dest.write_str("sticky") + dest.write_str("sticky")?; + Ok(()) } } } diff --git a/src/properties/size.rs b/src/properties/size.rs index 3c2494666..b76a72978 100644 --- a/src/properties/size.rs +++ b/src/properties/size.rs @@ -6,7 +6,6 @@ use crate::declaration::DeclarationList; use crate::error::{ParserError, PrinterError}; use crate::logical::PropertyCategory; use crate::macros::{enum_property, property_bitflags}; -use crate::printer::Printer; use crate::properties::{Property, PropertyId}; use crate::targets::Targets; use crate::traits::{IsCompatible, Parse, PropertyHandler, ToCss}; @@ -97,36 +96,52 @@ impl<'i> Parse<'i> for Size { } impl ToCss for Size { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { use Size::*; match self { - Auto => dest.write_str("auto"), - Contain => dest.write_str("contain"), + Auto => { + dest.write_str("auto")?; + Ok(()) + } + Contain => { + dest.write_str("contain")?; + Ok(()) + } MinContent(vp) => { vp.to_css(dest)?; - dest.write_str("min-content") + dest.write_str("min-content")?; + Ok(()) } MaxContent(vp) => { vp.to_css(dest)?; - dest.write_str("max-content") + dest.write_str("max-content")?; + Ok(()) } FitContent(vp) => { vp.to_css(dest)?; - dest.write_str("fit-content") + dest.write_str("fit-content")?; + Ok(()) } Stretch(vp) => match *vp { - VendorPrefix::None => dest.write_str("stretch"), - VendorPrefix::WebKit => dest.write_str("-webkit-fill-available"), - VendorPrefix::Moz => dest.write_str("-moz-available"), + VendorPrefix::None => { + dest.write_str("stretch")?; + Ok(()) + } + VendorPrefix::WebKit => { + dest.write_str("-webkit-fill-available")?; + Ok(()) + } + VendorPrefix::Moz => { + dest.write_str("-moz-available")?; + Ok(()) + } _ => unreachable!(), }, FitContentFunction(l) => { dest.write_str("fit-content(")?; l.to_css(dest)?; - dest.write_str(")") + dest.write_str(")")?; + Ok(()) } LengthPercentage(l) => l.to_css(dest), } @@ -230,36 +245,52 @@ impl<'i> Parse<'i> for MaxSize { } impl ToCss for MaxSize { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { use MaxSize::*; match self { - None => dest.write_str("none"), - Contain => dest.write_str("contain"), + None => { + dest.write_str("none")?; + Ok(()) + } + Contain => { + dest.write_str("contain")?; + Ok(()) + } MinContent(vp) => { vp.to_css(dest)?; - dest.write_str("min-content") + dest.write_str("min-content")?; + Ok(()) } MaxContent(vp) => { vp.to_css(dest)?; - dest.write_str("max-content") + dest.write_str("max-content")?; + Ok(()) } FitContent(vp) => { vp.to_css(dest)?; - dest.write_str("fit-content") + dest.write_str("fit-content")?; + Ok(()) } Stretch(vp) => match *vp { - VendorPrefix::None => dest.write_str("stretch"), - VendorPrefix::WebKit => dest.write_str("-webkit-fill-available"), - VendorPrefix::Moz => dest.write_str("-moz-available"), + VendorPrefix::None => { + dest.write_str("stretch")?; + Ok(()) + } + VendorPrefix::WebKit => { + dest.write_str("-webkit-fill-available")?; + Ok(()) + } + VendorPrefix::Moz => { + dest.write_str("-moz-available")?; + Ok(()) + } _ => unreachable!(), }, FitContentFunction(l) => { dest.write_str("fit-content(")?; l.to_css(dest)?; - dest.write_str(")") + dest.write_str(")")?; + Ok(()) } LengthPercentage(l) => l.to_css(dest), } @@ -403,10 +434,7 @@ impl<'i> Parse<'i> for AspectRatio { } impl ToCss for AspectRatio { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { if self.auto { dest.write_str("auto")?; } diff --git a/src/properties/svg.rs b/src/properties/svg.rs index f70d344c7..8a58ecf26 100644 --- a/src/properties/svg.rs +++ b/src/properties/svg.rs @@ -2,7 +2,6 @@ use crate::error::{ParserError, PrinterError}; use crate::macros::enum_property; -use crate::printer::Printer; use crate::targets::{Browsers, Targets}; use crate::traits::{FallbackValues, IsCompatible, Parse, ToCss}; use crate::values::length::LengthPercentage; @@ -170,12 +169,12 @@ impl<'i> Parse<'i> for StrokeDasharray { } impl ToCss for StrokeDasharray { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { - StrokeDasharray::None => dest.write_str("none"), + StrokeDasharray::None => { + dest.write_str("none")?; + Ok(()) + } StrokeDasharray::Values(values) => { let mut first = true; for value in values { diff --git a/src/properties/text.rs b/src/properties/text.rs index 5532212a4..315e674e5 100644 --- a/src/properties/text.rs +++ b/src/properties/text.rs @@ -9,7 +9,6 @@ use crate::declaration::{DeclarationBlock, DeclarationList}; use crate::error::{ParserError, PrinterError}; use crate::macros::{define_shorthand, enum_property}; use crate::prefixes::Feature; -use crate::printer::Printer; use crate::targets::{should_compile, Browsers, Targets}; use crate::traits::{FallbackValues, IsCompatible, Parse, PropertyHandler, Shorthand, ToCss, Zero}; use crate::values::calc::{Calc, MathFunction}; @@ -77,10 +76,7 @@ impl<'i> Parse<'i> for TextTransformOther { } impl ToCss for TextTransformOther { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { let mut needs_space = false; if self.contains(TextTransformOther::FullWidth) { dest.write_str("full-width")?; @@ -197,10 +193,7 @@ impl<'i> Parse<'i> for TextTransform { } impl ToCss for TextTransform { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { let mut needs_space = false; if self.case != TextTransformCase::None || self.other.is_empty() { self.case.to_css(dest)?; @@ -428,10 +421,7 @@ impl<'i> Parse<'i> for TextIndent { } impl ToCss for TextIndent { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { self.value.to_css(dest)?; if self.hanging { dest.write_str(" hanging")?; @@ -532,20 +522,17 @@ impl<'i> Parse<'i> for TextDecorationLine { } impl ToCss for TextDecorationLine { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { if self.is_empty() { - return dest.write_str("none"); + return Ok(dest.write_str("none")?); } if self.contains(TextDecorationLine::SpellingError) { - return dest.write_str("spelling-error"); + return Ok(dest.write_str("spelling-error")?); } if self.contains(TextDecorationLine::GrammarError) { - return dest.write_str("grammar-error"); + return Ok(dest.write_str("grammar-error")?); } let mut needs_space = false; @@ -773,10 +760,7 @@ impl<'i> Parse<'i> for TextDecoration { } impl ToCss for TextDecoration { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { self.line.to_css(dest)?; if self.line.is_empty() { return Ok(()); @@ -921,12 +905,12 @@ impl<'i> Parse<'i> for TextEmphasisStyle<'i> { } impl<'i> ToCss for TextEmphasisStyle<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { - TextEmphasisStyle::None => dest.write_str("none"), + TextEmphasisStyle::None => { + dest.write_str("none")?; + Ok(()) + } TextEmphasisStyle::String(s) => s.to_css(dest), TextEmphasisStyle::Keyword { fill, shape } => { let mut needs_space = false; @@ -989,10 +973,7 @@ impl<'i> Parse<'i> for TextEmphasis<'i> { } impl<'i> ToCss for TextEmphasis<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { self.style.to_css(dest)?; if self.style != TextEmphasisStyle::None && self.color != CssColor::current_color() { @@ -1084,10 +1065,7 @@ impl Default for BoxDecorationBreak { } impl ToCss for TextEmphasisPosition { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { self.vertical.to_css(dest)?; if self.horizontal != TextEmphasisPositionHorizontal::Right { dest.write_char(' ')?; @@ -1427,10 +1405,7 @@ impl<'i> Parse<'i> for TextShadow { } impl ToCss for TextShadow { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { self.x_offset.to_css(dest)?; dest.write_char(' ')?; self.y_offset.to_css(dest)?; diff --git a/src/properties/transform.rs b/src/properties/transform.rs index ab006c01c..2b1f67f78 100644 --- a/src/properties/transform.rs +++ b/src/properties/transform.rs @@ -6,7 +6,6 @@ use crate::declaration::DeclarationList; use crate::error::{ParserError, PrinterError}; use crate::macros::enum_property; use crate::prefixes::Feature; -use crate::printer::Printer; use crate::traits::{Parse, PropertyHandler, ToCss, Zero}; use crate::values::number::NonNegative; use crate::values::{ @@ -48,10 +47,7 @@ impl<'i> Parse<'i> for TransformList { } impl ToCss for TransformList { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { if self.0.is_empty() { dest.write_str("none")?; return Ok(()); @@ -59,7 +55,7 @@ impl ToCss for TransformList { // TODO: Re-enable with a better solution // See: https://github.com/parcel-bundler/lightningcss/issues/288 - // if dest.minify { + // if dest.options().minify { // // Combine transforms into a single matrix. // if let Some(matrix) = self.to_matrix() { // // Generate based on the original transforms. @@ -123,10 +119,7 @@ impl ToCss for TransformList { } impl TransformList { - fn to_css_base(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css_base(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { let mut first = true; for item in &self.0 { if first { @@ -1023,14 +1016,11 @@ impl<'i> Parse<'i> for Transform { } impl ToCss for Transform { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { use Transform::*; match self { Translate(x, y) => { - if dest.minify && x.is_zero() && !y.is_zero() { + if dest.options().minify && x.is_zero() && !y.is_zero() { dest.write_str("translateY(")?; y.to_css(dest)? } else { @@ -1041,34 +1031,42 @@ impl ToCss for Transform { y.to_css(dest)?; } } - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } TranslateX(x) => { - dest.write_str(if dest.minify { "translate(" } else { "translateX(" })?; + dest.write_str(if dest.options().minify { + "translate(" + } else { + "translateX(" + })?; x.to_css(dest)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } TranslateY(y) => { dest.write_str("translateY(")?; y.to_css(dest)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } TranslateZ(z) => { dest.write_str("translateZ(")?; z.to_css(dest)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } Translate3d(x, y, z) => { - if dest.minify && !x.is_zero() && y.is_zero() && z.is_zero() { + if dest.options().minify && !x.is_zero() && y.is_zero() && z.is_zero() { dest.write_str("translate(")?; x.to_css(dest)?; - } else if dest.minify && x.is_zero() && !y.is_zero() && z.is_zero() { + } else if dest.options().minify && x.is_zero() && !y.is_zero() && z.is_zero() { dest.write_str("translateY(")?; y.to_css(dest)?; - } else if dest.minify && x.is_zero() && y.is_zero() && !z.is_zero() { + } else if dest.options().minify && x.is_zero() && y.is_zero() && !z.is_zero() { dest.write_str("translateZ(")?; z.to_css(dest)?; - } else if dest.minify && z.is_zero() { + } else if dest.options().minify && z.is_zero() { dest.write_str("translate(")?; x.to_css(dest)?; dest.delim(',', false)?; @@ -1081,15 +1079,16 @@ impl ToCss for Transform { dest.delim(',', false)?; z.to_css(dest)?; } - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } Scale(x, y) => { let x: f32 = x.into(); let y: f32 = y.into(); - if dest.minify && x == 1.0 && y != 1.0 { + if dest.options().minify && x == 1.0 && y != 1.0 { dest.write_str("scaleY(")?; y.to_css(dest)?; - } else if dest.minify && x != 1.0 && y == 1.0 { + } else if dest.options().minify && x != 1.0 && y == 1.0 { dest.write_str("scaleX(")?; x.to_css(dest)?; } else { @@ -1100,47 +1099,51 @@ impl ToCss for Transform { y.to_css(dest)?; } } - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } ScaleX(x) => { let x: f32 = x.into(); dest.write_str("scaleX(")?; x.to_css(dest)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } ScaleY(y) => { let y: f32 = y.into(); dest.write_str("scaleY(")?; y.to_css(dest)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } ScaleZ(z) => { let z: f32 = z.into(); dest.write_str("scaleZ(")?; z.to_css(dest)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } Scale3d(x, y, z) => { let x: f32 = x.into(); let y: f32 = y.into(); let z: f32 = z.into(); - if dest.minify && z == 1.0 && x == y { + if dest.options().minify && z == 1.0 && x == y { // scale3d(x, x, 1) => scale(x) dest.write_str("scale(")?; x.to_css(dest)?; - } else if dest.minify && x != 1.0 && y == 1.0 && z == 1.0 { + } else if dest.options().minify && x != 1.0 && y == 1.0 && z == 1.0 { // scale3d(x, 1, 1) => scaleX(x) dest.write_str("scaleX(")?; x.to_css(dest)?; - } else if dest.minify && x == 1.0 && y != 1.0 && z == 1.0 { + } else if dest.options().minify && x == 1.0 && y != 1.0 && z == 1.0 { // scale3d(1, y, 1) => scaleY(y) dest.write_str("scaleY(")?; y.to_css(dest)?; - } else if dest.minify && x == 1.0 && y == 1.0 && z != 1.0 { + } else if dest.options().minify && x == 1.0 && y == 1.0 && z != 1.0 { // scale3d(1, 1, z) => scaleZ(z) dest.write_str("scaleZ(")?; z.to_css(dest)?; - } else if dest.minify && z == 1.0 { + } else if dest.options().minify && z == 1.0 { // scale3d(x, y, 1) => scale(x, y) dest.write_str("scale(")?; x.to_css(dest)?; @@ -1154,38 +1157,43 @@ impl ToCss for Transform { dest.delim(',', false)?; z.to_css(dest)?; } - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } Rotate(angle) => { dest.write_str("rotate(")?; angle.to_css_with_unitless_zero(dest)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } RotateX(angle) => { dest.write_str("rotateX(")?; angle.to_css_with_unitless_zero(dest)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } RotateY(angle) => { dest.write_str("rotateY(")?; angle.to_css_with_unitless_zero(dest)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } RotateZ(angle) => { - dest.write_str(if dest.minify { "rotate(" } else { "rotateZ(" })?; + dest.write_str(if dest.options().minify { "rotate(" } else { "rotateZ(" })?; angle.to_css_with_unitless_zero(dest)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } Rotate3d(x, y, z, angle) => { - if dest.minify && *x == 1.0 && *y == 0.0 && *z == 0.0 { + if dest.options().minify && *x == 1.0 && *y == 0.0 && *z == 0.0 { // rotate3d(1, 0, 0, a) => rotateX(a) dest.write_str("rotateX(")?; angle.to_css_with_unitless_zero(dest)?; - } else if dest.minify && *x == 0.0 && *y == 1.0 && *z == 0.0 { + } else if dest.options().minify && *x == 0.0 && *y == 1.0 && *z == 0.0 { // rotate3d(0, 1, 0, a) => rotateY(a) dest.write_str("rotateY(")?; angle.to_css_with_unitless_zero(dest)?; - } else if dest.minify && *x == 0.0 && *y == 0.0 && *z == 1.0 { + } else if dest.options().minify && *x == 0.0 && *y == 0.0 && *z == 1.0 { // rotate3d(0, 0, 1, a) => rotate(a) dest.write_str("rotate(")?; angle.to_css_with_unitless_zero(dest)?; @@ -1199,10 +1207,11 @@ impl ToCss for Transform { dest.delim(',', false)?; angle.to_css_with_unitless_zero(dest)?; } - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } Skew(x, y) => { - if dest.minify && x.is_zero() && !y.is_zero() { + if dest.options().minify && x.is_zero() && !y.is_zero() { dest.write_str("skewY(")?; y.to_css_with_unitless_zero(dest)? } else { @@ -1213,22 +1222,26 @@ impl ToCss for Transform { y.to_css_with_unitless_zero(dest)?; } } - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } SkewX(angle) => { - dest.write_str(if dest.minify { "skew(" } else { "skewX(" })?; + dest.write_str(if dest.options().minify { "skew(" } else { "skewX(" })?; angle.to_css_with_unitless_zero(dest)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } SkewY(angle) => { dest.write_str("skewY(")?; angle.to_css_with_unitless_zero(dest)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } Perspective(len) => { dest.write_str("perspective(")?; len.to_css(dest)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } Matrix(super::transform::Matrix { a, b, c, d, e, f }) => { dest.write_str("matrix(")?; @@ -1243,7 +1256,8 @@ impl ToCss for Transform { e.to_css(dest)?; dest.delim(',', false)?; f.to_css(dest)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } Matrix3d(super::transform::Matrix3d { m11, @@ -1295,7 +1309,8 @@ impl ToCss for Transform { m43.to_css(dest)?; dest.delim(',', false)?; m44.to_css(dest)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } } } @@ -1473,10 +1488,7 @@ impl<'i> Parse<'i> for Translate { } impl ToCss for Translate { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { Translate::None => { dest.write_str("none")?; @@ -1574,12 +1586,12 @@ impl<'i> Parse<'i> for Rotate { } impl ToCss for Rotate { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { - Rotate::None => dest.write_str("none"), + Rotate::None => { + dest.write_str("none")?; + Ok(()) + } Rotate::XYZ { x, y, z, angle } => { // CSS Transforms 2 §5.1: // "If the axis is parallel with the x or y axes, it must serialize as the appropriate keyword." @@ -1679,10 +1691,7 @@ impl<'i> Parse<'i> for Scale { } impl ToCss for Scale { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { Scale::None => { dest.write_str("none")?; diff --git a/src/properties/transition.rs b/src/properties/transition.rs index 599c365c2..94b414167 100644 --- a/src/properties/transition.rs +++ b/src/properties/transition.rs @@ -7,7 +7,6 @@ use crate::declaration::{DeclarationBlock, DeclarationList}; use crate::error::{ParserError, PrinterError}; use crate::macros::define_list_shorthand; use crate::prefixes::Feature; -use crate::printer::Printer; use crate::properties::masking::get_webkit_mask_property; use crate::traits::{Parse, PropertyHandler, Shorthand, ToCss, Zero}; use crate::values::ident::CustomIdent; @@ -85,10 +84,7 @@ impl<'i> Parse<'i> for Transition<'i> { } impl<'i> ToCss for Transition<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { self.property.to_css(dest)?; if !self.duration.is_zero() || !self.delay.is_zero() { dest.write_char(' ')?; diff --git a/src/properties/ui.rs b/src/properties/ui.rs index e0802c500..be2c7ba36 100644 --- a/src/properties/ui.rs +++ b/src/properties/ui.rs @@ -4,7 +4,6 @@ use crate::context::PropertyHandlerContext; use crate::declaration::{DeclarationBlock, DeclarationList}; use crate::error::{ParserError, PrinterError}; use crate::macros::{define_shorthand, enum_property, shorthand_property}; -use crate::printer::Printer; use crate::properties::{Property, PropertyId}; use crate::targets::{should_compile, Browsers, Targets}; use crate::traits::{FallbackValues, IsCompatible, Parse, PropertyHandler, Shorthand, ToCss}; @@ -70,10 +69,7 @@ impl<'i> Parse<'i> for CursorImage<'i> { } impl<'i> ToCss for CursorImage<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { self.url.to_css(dest)?; if let Some((x, y)) = self.hotspot { @@ -165,10 +161,7 @@ impl<'i> Parse<'i> for Cursor<'i> { } impl<'i> ToCss for Cursor<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { for image in &self.images { image.to_css(dest)?; dest.delim(',', false)?; @@ -367,11 +360,9 @@ impl<'i> Parse<'i> for Appearance<'i> { } impl<'i> ToCss for Appearance<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - dest.write_str(self.to_str()) + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { + dest.write_str(self.to_str())?; + Ok(()) } } @@ -475,12 +466,9 @@ impl<'i> Parse<'i> for ColorScheme { } impl ToCss for ColorScheme { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { if self.is_empty() { - return dest.write_str("normal"); + return Ok(dest.write_str("normal")?); } if self.contains(ColorScheme::Light) { @@ -497,7 +485,7 @@ impl ToCss for ColorScheme { if self.contains(ColorScheme::Only) { // Avoid parsing `color-scheme: only` as `color-scheme: only` if !self.intersects(ColorScheme::Light | ColorScheme::Dark) { - return dest.write_str("only"); + return Ok(dest.write_str("only")?); } dest.write_str(" only")?; } diff --git a/src/rules/container.rs b/src/rules/container.rs index cd4cc03e2..d3d101f49 100644 --- a/src/rules/container.rs +++ b/src/rules/container.rs @@ -10,7 +10,6 @@ use crate::media_query::{ MediaFeatureType, Operator, QueryCondition, QueryConditionFlags, QueryFeature, ValueType, }; use crate::parser::{DefaultAtRule, ParserOptions}; -use crate::printer::Printer; use crate::properties::custom::TokenList; use crate::properties::{Property, PropertyId}; #[cfg(feature = "serde")] @@ -99,10 +98,11 @@ define_query_features! { } impl FeatureToCss for ContainerSizeFeatureId { - fn to_css_with_prefix(&self, prefix: &str, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css_with_prefix( + &self, + prefix: &str, + dest: &mut PrinterT, + ) -> Result<(), PrinterError> { dest.write_str(prefix)?; self.to_css(dest) } @@ -186,10 +186,11 @@ define_query_features! { } impl FeatureToCss for ScrollStateFeatureId { - fn to_css_with_prefix(&self, prefix: &str, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css_with_prefix( + &self, + prefix: &str, + dest: &mut PrinterT, + ) -> Result<(), PrinterError> { dest.write_str(prefix)?; self.to_css(dest) } @@ -349,15 +350,12 @@ impl<'i> ParseWithOptions<'i> for ContainerCondition<'i> { } impl<'i> ToCss for ContainerCondition<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match *self { ContainerCondition::Feature(ref f) => f.to_css(dest), ContainerCondition::Not(ref c) => { dest.write_str("not ")?; - to_css_with_parens_if_needed(&**c, dest, c.needs_parens(None, &dest.targets.current)) + to_css_with_parens_if_needed(&**c, dest, c.needs_parens(None, &dest.state().targets.current)) } ContainerCondition::Operation { ref conditions, @@ -366,7 +364,8 @@ impl<'i> ToCss for ContainerCondition<'i> { ContainerCondition::Style(ref query) => { dest.write_str("style(")?; query.to_css(dest)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } ContainerCondition::ScrollState(ref query) => { let needs_parens = !matches!(query, ScrollStateQuery::Feature(_)); @@ -386,16 +385,13 @@ impl<'i> ToCss for ContainerCondition<'i> { } impl<'i> ToCss for StyleQuery<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match *self { StyleQuery::Declaration(ref f) => f.to_css(dest, false), StyleQuery::Property(ref f) => f.to_css(dest), StyleQuery::Not(ref c) => { dest.write_str("not ")?; - to_css_with_parens_if_needed(&**c, dest, c.needs_parens(None, &dest.targets.current)) + to_css_with_parens_if_needed(&**c, dest, c.needs_parens(None, &dest.state().targets.current)) } StyleQuery::Operation { ref conditions, @@ -406,15 +402,12 @@ impl<'i> ToCss for StyleQuery<'i> { } impl<'i> ToCss for ScrollStateQuery<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match *self { ScrollStateQuery::Feature(ref f) => f.to_css(dest), ScrollStateQuery::Not(ref c) => { dest.write_str("not ")?; - to_css_with_parens_if_needed(&**c, dest, c.needs_parens(None, &dest.targets.current)) + to_css_with_parens_if_needed(&**c, dest, c.needs_parens(None, &dest.state().targets.current)) } ScrollStateQuery::Operation { ref conditions, @@ -443,15 +436,12 @@ impl<'i> Parse<'i> for ContainerName<'i> { } impl<'i> ToCss for ContainerName<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { // Container name should not be hashed // https://github.com/vercel/next.js/issues/71233 self.0.to_css_with_options( dest, - match &dest.css_module { + match dest.state().css_module.as_ref() { Some(css_module) => css_module.config.container, None => false, }, @@ -471,11 +461,7 @@ impl<'i, T: Clone> ContainerRule<'i, T> { } impl<'a, 'i, T: ToCss> ToCss for ContainerRule<'i, T> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - #[cfg(feature = "sourcemap")] + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { dest.add_mapping(self.loc); dest.write_str("@container ")?; let has_condition = self.condition.is_some(); @@ -489,10 +475,11 @@ impl<'a, 'i, T: ToCss> ToCss for ContainerRule<'i, T> { if let Some(condition) = &self.condition { // Don't downlevel range syntax in container queries. - let exclude = dest.targets.current.exclude; - dest.targets.current.exclude.insert(Features::MediaQueries); - condition.to_css(dest)?; - dest.targets.current.exclude = exclude; + let exclude = dest.state().targets.current.exclude; + dest.state_mut().targets.current.exclude.insert(Features::MediaQueries); + let res = condition.to_css(dest); + dest.state_mut().targets.current.exclude = exclude; + res?; } dest.whitespace()?; @@ -502,6 +489,7 @@ impl<'a, 'i, T: ToCss> ToCss for ContainerRule<'i, T> { self.rules.to_css(dest)?; dest.dedent(); dest.newline()?; - dest.write_char('}') + dest.write_char('}')?; + Ok(()) } } diff --git a/src/rules/counter_style.rs b/src/rules/counter_style.rs index bb54707f5..70b7ae2e0 100644 --- a/src/rules/counter_style.rs +++ b/src/rules/counter_style.rs @@ -3,7 +3,6 @@ use super::Location; use crate::declaration::DeclarationBlock; use crate::error::PrinterError; -use crate::printer::Printer; use crate::traits::ToCss; use crate::values::ident::CustomIdent; #[cfg(feature = "visitor")] @@ -28,11 +27,7 @@ pub struct CounterStyleRule<'i> { } impl<'i> ToCss for CounterStyleRule<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - #[cfg(feature = "sourcemap")] + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { dest.add_mapping(self.loc); dest.write_str("@counter-style ")?; self.name.to_css(dest)?; diff --git a/src/rules/custom_media.rs b/src/rules/custom_media.rs index 130429626..d2f90fb56 100644 --- a/src/rules/custom_media.rs +++ b/src/rules/custom_media.rs @@ -3,7 +3,6 @@ use super::Location; use crate::error::PrinterError; use crate::media_query::MediaList; -use crate::printer::Printer; use crate::traits::ToCss; use crate::values::ident::DashedIdent; #[cfg(feature = "visitor")] @@ -27,16 +26,13 @@ pub struct CustomMediaRule<'i> { } impl<'i> ToCss for CustomMediaRule<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - #[cfg(feature = "sourcemap")] + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { dest.add_mapping(self.loc); dest.write_str("@custom-media ")?; self.name.to_css(dest)?; dest.write_char(' ')?; self.query.to_css(dest)?; - dest.write_char(';') + dest.write_char(';')?; + Ok(()) } } diff --git a/src/rules/document.rs b/src/rules/document.rs index e118ec4bd..8adf500a3 100644 --- a/src/rules/document.rs +++ b/src/rules/document.rs @@ -4,7 +4,6 @@ use super::Location; use super::{CssRuleList, MinifyContext}; use crate::error::{MinifyError, PrinterError}; use crate::parser::DefaultAtRule; -use crate::printer::Printer; use crate::traits::ToCss; #[cfg(feature = "visitor")] use crate::visitor::Visit; @@ -34,11 +33,7 @@ impl<'i, T: Clone> MozDocumentRule<'i, T> { } impl<'i, T: ToCss> ToCss for MozDocumentRule<'i, T> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - #[cfg(feature = "sourcemap")] + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { dest.add_mapping(self.loc); dest.write_str("@-moz-document url-prefix()")?; dest.whitespace()?; @@ -48,6 +43,7 @@ impl<'i, T: ToCss> ToCss for MozDocumentRule<'i, T> { self.rules.to_css(dest)?; dest.dedent(); dest.newline()?; - dest.write_char('}') + dest.write_char('}')?; + Ok(()) } } diff --git a/src/rules/font_face.rs b/src/rules/font_face.rs index 72a3c441d..06b153f3b 100644 --- a/src/rules/font_face.rs +++ b/src/rules/font_face.rs @@ -3,7 +3,6 @@ use super::Location; use crate::error::{ParserError, PrinterError}; use crate::macros::enum_property; -use crate::printer::Printer; use crate::properties::custom::CustomProperty; use crate::properties::font::{FontFamily, FontStretch, FontStyle as FontStyleProperty, FontWeight}; use crate::stylesheet::ParserOptions; @@ -15,7 +14,6 @@ use crate::values::url::Url; #[cfg(feature = "visitor")] use crate::visitor::Visit; use cssparser::*; -use std::fmt::Write; /// A [@font-face](https://drafts.csswg.org/css-fonts/#font-face-rule) rule. #[derive(Debug, PartialEq, Clone)] @@ -101,16 +99,14 @@ impl<'i> Parse<'i> for Source<'i> { } impl<'i> ToCss for Source<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { Source::Url(url) => url.to_css(dest), Source::Local(local) => { dest.write_str("local(")?; local.to_css(dest)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } } } @@ -154,10 +150,7 @@ impl<'i> Parse<'i> for UrlSource<'i> { } impl<'i> ToCss for UrlSource<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { self.url.to_css(dest)?; if let Some(format) = &self.format { dest.whitespace()?; @@ -227,10 +220,7 @@ impl<'i> Parse<'i> for FontFormat<'i> { } impl<'i> ToCss for FontFormat<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { use FontFormat::*; let s = match self { WOFF => "woff", @@ -319,10 +309,7 @@ impl<'i> Parse<'i> for UnicodeRange { } impl ToCss for UnicodeRange { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { // Attempt to optimize the range to use question mark syntax. if self.start != self.end { // Find the first hex digit that differs between the start and end values. @@ -420,13 +407,16 @@ impl<'i> Parse<'i> for FontStyle { } impl ToCss for FontStyle { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { - FontStyle::Normal => dest.write_str("normal"), - FontStyle::Italic => dest.write_str("italic"), + FontStyle::Normal => { + dest.write_str("normal")?; + Ok(()) + } + FontStyle::Italic => { + dest.write_str("italic")?; + Ok(()) + } FontStyle::Oblique(angle) => { dest.write_str("oblique")?; if *angle != FontStyle::default_oblique_angle() { @@ -510,11 +500,7 @@ impl<'i> RuleBodyItemParser<'i, FontFaceProperty<'i>, ParserError<'i>> for FontF } impl<'i> ToCss for FontFaceRule<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - #[cfg(feature = "sourcemap")] + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { dest.add_mapping(self.loc); dest.write_str("@font-face")?; dest.whitespace()?; @@ -524,21 +510,19 @@ impl<'i> ToCss for FontFaceRule<'i> { for (i, prop) in self.properties.iter().enumerate() { dest.newline()?; prop.to_css(dest)?; - if i != len - 1 || !dest.minify { + if i != len - 1 || !dest.options().minify { dest.write_char(';')?; } } dest.dedent(); dest.newline()?; - dest.write_char('}') + dest.write_char('}')?; + Ok(()) } } impl<'i> ToCss for FontFaceProperty<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { use FontFaceProperty::*; macro_rules! property { ($prop: literal, $value: expr) => {{ diff --git a/src/rules/font_feature_values.rs b/src/rules/font_feature_values.rs index 0e5bbf5b1..9a3c2a612 100644 --- a/src/rules/font_feature_values.rs +++ b/src/rules/font_feature_values.rs @@ -3,7 +3,6 @@ use super::Location; use crate::error::{ParserError, PrinterError}; use crate::parser::ParserOptions; -use crate::printer::Printer; use crate::properties::font::FamilyName; use crate::traits::{Parse, ToCss}; use crate::values::ident::Ident; @@ -13,7 +12,6 @@ use crate::visitor::Visit; use cssparser::*; use indexmap::IndexMap; use smallvec::SmallVec; -use std::fmt::Write; /// A [@font-feature-values](https://drafts.csswg.org/css-fonts/#font-feature-values) rule. #[derive(Debug, PartialEq, Clone)] @@ -152,11 +150,7 @@ impl<'a, 'i> RuleBodyItemParser<'i, (), ParserError<'i>> for FontFeatureValuesRu } impl<'i> ToCss for FontFeatureValuesRule<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - #[cfg(feature = "sourcemap")] + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { dest.add_mapping(self.loc); dest.write_str("@font-feature-values ")?; self.name.to_css(dest)?; @@ -169,7 +163,8 @@ impl<'i> ToCss for FontFeatureValuesRule<'i> { dest.newline()?; } } - dest.write_char('}') + dest.write_char('}')?; + Ok(()) } } @@ -240,11 +235,7 @@ pub struct FontFeatureSubrule<'i> { } impl<'i> ToCss for FontFeatureSubrule<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: Write, - { - #[cfg(feature = "sourcemap")] + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { dest.add_mapping(self.loc); dest.write_char('@')?; self.name.to_css(dest)?; @@ -266,13 +257,14 @@ impl<'i> ToCss for FontFeatureSubrule<'i> { index.to_css(dest)?; } - if i != len - 1 || !dest.minify { + if i != len - 1 || !dest.options().minify { dest.write_char(';')?; } } dest.dedent(); dest.newline()?; - dest.write_char('}') + dest.write_char('}')?; + Ok(()) } } diff --git a/src/rules/font_palette_values.rs b/src/rules/font_palette_values.rs index ad08349db..b64964f64 100644 --- a/src/rules/font_palette_values.rs +++ b/src/rules/font_palette_values.rs @@ -3,7 +3,6 @@ use super::supports::SupportsRule; use super::{CssRule, CssRuleList, Location, MinifyContext}; use crate::error::{ParserError, PrinterError}; -use crate::printer::Printer; use crate::properties::custom::CustomProperty; use crate::properties::font::FontFamily; use crate::stylesheet::ParserOptions; @@ -202,13 +201,16 @@ impl<'i> Parse<'i> for BasePalette { } impl ToCss for BasePalette { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { - BasePalette::Light => dest.write_str("light"), - BasePalette::Dark => dest.write_str("dark"), + BasePalette::Light => { + dest.write_str("light")?; + Ok(()) + } + BasePalette::Dark => { + dest.write_str("dark")?; + Ok(()) + } BasePalette::Integer(i) => (*i as CSSInteger).to_css(dest), } } @@ -234,10 +236,7 @@ impl<'i> Parse<'i> for OverrideColors { } impl ToCss for OverrideColors { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { (self.index as CSSInteger).to_css(dest)?; dest.write_char(' ')?; self.color.to_css(dest) @@ -358,11 +357,7 @@ impl<'i> FontPaletteValuesRule<'i> { } impl<'i> ToCss for FontPaletteValuesRule<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - #[cfg(feature = "sourcemap")] + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { dest.add_mapping(self.loc); dest.write_str("@font-palette-values ")?; self.name.to_css(dest)?; @@ -373,21 +368,19 @@ impl<'i> ToCss for FontPaletteValuesRule<'i> { for (i, prop) in self.properties.iter().enumerate() { dest.newline()?; prop.to_css(dest)?; - if i != len - 1 || !dest.minify { + if i != len - 1 || !dest.options().minify { dest.write_char(';')?; } } dest.dedent(); dest.newline()?; - dest.write_char('}') + dest.write_char('}')?; + Ok(()) } } impl<'i> ToCss for FontPaletteValuesProperty<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { macro_rules! property { ($prop: literal, $value: expr) => {{ dest.write_str($prop)?; diff --git a/src/rules/import.rs b/src/rules/import.rs index e240a6d46..4109dd17f 100644 --- a/src/rules/import.rs +++ b/src/rules/import.rs @@ -6,7 +6,6 @@ use super::Location; use crate::dependencies::{Dependency, ImportDependency}; use crate::error::PrinterError; use crate::media_query::MediaList; -use crate::printer::Printer; use crate::traits::ToCss; use crate::values::string::CowArcStr; #[cfg(feature = "visitor")] @@ -38,23 +37,18 @@ pub struct ImportRule<'i> { } impl<'i> ToCss for ImportRule<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - let dep = if dest.dependencies.is_some() { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { + let dep = if dest.state().dependencies.is_some() { Some(ImportDependency::new(self, dest.filename())) } else { None }; - - #[cfg(feature = "sourcemap")] dest.add_mapping(self.loc); dest.write_str("@import ")?; if let Some(dep) = dep { serialize_string(&dep.placeholder, dest)?; - if let Some(dependencies) = &mut dest.dependencies { + if let Some(dependencies) = dest.state_mut().dependencies.as_mut() { dependencies.push(Dependency::Import(dep)) } } else { @@ -84,6 +78,7 @@ impl<'i> ToCss for ImportRule<'i> { dest.write_char(' ')?; self.media.to_css(dest)?; } - dest.write_str(";") + dest.write_str(";")?; + Ok(()) } } diff --git a/src/rules/keyframes.rs b/src/rules/keyframes.rs index 02d0943b6..9f1257fc8 100644 --- a/src/rules/keyframes.rs +++ b/src/rules/keyframes.rs @@ -7,7 +7,6 @@ use crate::context::DeclarationContext; use crate::declaration::DeclarationBlock; use crate::error::{ParserError, PrinterError}; use crate::parser::ParserOptions; -use crate::printer::Printer; use crate::properties::animation::TimelineRangeName; use crate::properties::custom::{CustomProperty, UnparsedProperty}; use crate::properties::Property; @@ -89,12 +88,12 @@ impl<'i> Parse<'i> for KeyframesName<'i> { } impl<'i> ToCss for KeyframesName<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - let css_module_animation_enabled = - dest.css_module.as_ref().map_or(false, |css_module| css_module.config.animation); + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { + let css_module_animation_enabled = dest + .state() + .css_module + .as_ref() + .map_or(false, |css_module| css_module.config.animation); match self { KeyframesName::Ident(ident) => { @@ -215,11 +214,7 @@ impl<'i> KeyframesRule<'i> { } impl<'i> ToCss for KeyframesRule<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - #[cfg(feature = "sourcemap")] + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { dest.add_mapping(self.loc); let mut first_rule = true; macro_rules! write_prefix { @@ -229,7 +224,7 @@ impl<'i> ToCss for KeyframesRule<'i> { if first_rule { first_rule = false; } else { - if !dest.minify { + if !dest.options().minify { dest.write_char('\n')?; // no indent } dest.newline()?; @@ -245,7 +240,7 @@ impl<'i> ToCss for KeyframesRule<'i> { for keyframe in &self.keyframes { if first { first = false; - } else if !dest.minify { + } else if !dest.options().minify { dest.write_char('\n')?; // no indent } dest.newline()?; @@ -314,26 +309,29 @@ pub enum KeyframeSelector { } impl ToCss for KeyframeSelector { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { KeyframeSelector::Percentage(p) => { - if dest.minify && *p == Percentage(1.0) { - dest.write_str("to") + if dest.options().minify && *p == Percentage(1.0) { + dest.write_str("to")?; + Ok(()) } else { p.to_css(dest) } } KeyframeSelector::From => { - if dest.minify { - dest.write_str("0%") + if dest.options().minify { + dest.write_str("0%")?; + Ok(()) } else { - dest.write_str("from") + dest.write_str("from")?; + Ok(()) } } - KeyframeSelector::To => dest.write_str("to"), + KeyframeSelector::To => { + dest.write_str("to")?; + Ok(()) + } KeyframeSelector::TimelineRangePercentage(TimelineRangePercentage { name: timeline_range_name, percentage, @@ -363,10 +361,7 @@ pub struct Keyframe<'i> { } impl<'i> ToCss for Keyframe<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { let mut first = true; for selector in &self.selectors { if !first { diff --git a/src/rules/layer.rs b/src/rules/layer.rs index 9a66e5387..9f977133a 100644 --- a/src/rules/layer.rs +++ b/src/rules/layer.rs @@ -3,7 +3,6 @@ use super::{CssRuleList, Location, MinifyContext}; use crate::error::{MinifyError, ParserError, PrinterError}; use crate::parser::DefaultAtRule; -use crate::printer::Printer; use crate::traits::{Parse, ToCss}; use crate::values::string::CowArcStr; #[cfg(feature = "visitor")] @@ -61,10 +60,7 @@ impl<'i> Parse<'i> for LayerName<'i> { } impl<'i> ToCss for LayerName<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { let mut first = true; for name in &self.0 { if first { @@ -99,15 +95,12 @@ pub struct LayerStatementRule<'i> { } impl<'i> ToCss for LayerStatementRule<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - #[cfg(feature = "sourcemap")] + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { dest.add_mapping(self.loc); dest.write_str("@layer ")?; self.names.to_css(dest)?; - dest.write_char(';') + dest.write_char(';')?; + Ok(()) } } @@ -142,11 +135,7 @@ impl<'i, T: Clone> LayerBlockRule<'i, T> { } impl<'a, 'i, T: ToCss> ToCss for LayerBlockRule<'i, T> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - #[cfg(feature = "sourcemap")] + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { dest.add_mapping(self.loc); dest.write_str("@layer")?; if let Some(name) = &self.name { @@ -161,6 +150,7 @@ impl<'a, 'i, T: ToCss> ToCss for LayerBlockRule<'i, T> { self.rules.to_css(dest)?; dest.dedent(); dest.newline()?; - dest.write_char('}') + dest.write_char('}')?; + Ok(()) } } diff --git a/src/rules/media.rs b/src/rules/media.rs index c398b9b01..bd0de95a1 100644 --- a/src/rules/media.rs +++ b/src/rules/media.rs @@ -5,7 +5,6 @@ use super::{CssRuleList, MinifyContext}; use crate::error::{MinifyError, PrinterError}; use crate::media_query::MediaList; use crate::parser::DefaultAtRule; -use crate::printer::Printer; use crate::traits::ToCss; #[cfg(feature = "visitor")] use crate::visitor::Visit; @@ -45,17 +44,12 @@ impl<'i, T: Clone> MediaRule<'i, T> { } impl<'a, 'i, T: ToCss> ToCss for MediaRule<'i, T> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { // If the media query always matches, we can just output the nested rules. - if dest.minify && self.query.always_matches() { + if dest.options().minify && self.query.always_matches() { self.rules.to_css(dest)?; return Ok(()); } - - #[cfg(feature = "sourcemap")] dest.add_mapping(self.loc); dest.write_str("@media ")?; self.query.to_css(dest)?; @@ -66,6 +60,7 @@ impl<'a, 'i, T: ToCss> ToCss for MediaRule<'i, T> { self.rules.to_css(dest)?; dest.dedent(); dest.newline()?; - dest.write_char('}') + dest.write_char('}')?; + Ok(()) } } diff --git a/src/rules/mod.rs b/src/rules/mod.rs index 1e35a4635..5920215e9 100644 --- a/src/rules/mod.rs +++ b/src/rules/mod.rs @@ -70,7 +70,6 @@ use crate::dependencies::{Dependency, ImportDependency}; use crate::error::{MinifyError, ParserError, PrinterError, PrinterErrorKind}; use crate::parser::{parse_rule_list, parse_style_block, DefaultAtRule, DefaultAtRuleParser, TopLevelRuleParser}; use crate::prefixes::Feature; -use crate::printer::Printer; use crate::rules::keyframes::KeyframesName; use crate::selector::{is_compatible, is_equivalent, Component, Selector, SelectorList}; use crate::stylesheet::ParserOptions; @@ -111,6 +110,18 @@ pub(crate) struct StyleContext<'a, 'i> { pub parent: Option<&'a StyleContext<'a, 'i>>, } +pub(crate) type StyleContextPtr = *const StyleContext<'static, 'static>; + +pub(crate) fn style_context_ptr(context: &StyleContext<'_, '_>) -> StyleContextPtr { + context as *const _ as StyleContextPtr +} + +pub(crate) fn detach_style_context( + context: Option, +) -> Option<&'static StyleContext<'static, 'static>> { + context.map(|ptr| unsafe { &*ptr }) +} + /// A source location. #[derive(PartialEq, Eq, Debug, Clone, Copy)] #[cfg_attr(any(feature = "serde", feature = "nodejs"), derive(serde::Serialize))] @@ -378,10 +389,7 @@ impl<'i, 'de: 'i, R: serde::Deserialize<'de>> serde::Deserialize<'de> for CssRul } impl<'a, 'i, T: ToCss> ToCss for CssRule<'i, T> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { CssRule::Media(media) => media.to_css(dest), CssRule::Import(import) => import.to_css(dest), @@ -1108,10 +1116,7 @@ fn merge_style_rules<'i, T>( } impl<'a, 'i, T: ToCss> ToCss for CssRuleList<'i, T> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { let mut first = true; let mut last_without_block = false; @@ -1122,14 +1127,14 @@ impl<'a, 'i, T: ToCss> ToCss for CssRuleList<'i, T> { // Skip @import rules if collecting dependencies. if let CssRule::Import(rule) = &rule { - if dest.remove_imports { - let dep = if dest.dependencies.is_some() { + if dest.state().remove_imports { + let dep = if dest.state().dependencies.is_some() { Some(Dependency::Import(ImportDependency::new(&rule, dest.filename()))) } else { None }; - if let Some(dependencies) = &mut dest.dependencies { + if let Some(dependencies) = dest.state_mut().dependencies.as_mut() { dependencies.push(dep.unwrap()); continue; } @@ -1139,7 +1144,7 @@ impl<'a, 'i, T: ToCss> ToCss for CssRuleList<'i, T> { if first { first = false; } else { - if !dest.minify + if !dest.options().minify && !(last_without_block && matches!( rule, @@ -1153,8 +1158,8 @@ impl<'a, 'i, T: ToCss> ToCss for CssRuleList<'i, T> { rule.to_css(dest)?; // If this is an invisible nested declarations rule, and not the last rule in the block, add a semicolon. - if dest.minify - && !should_compile!(dest.targets.current, Nesting) + if dest.options().minify + && !should_compile!(dest.state().targets.current, Nesting) && matches!(rule, CssRule::NestedDeclarations(_)) && i != self.0.len() - 1 { diff --git a/src/rules/namespace.rs b/src/rules/namespace.rs index c6c66a56a..58c70b034 100644 --- a/src/rules/namespace.rs +++ b/src/rules/namespace.rs @@ -2,7 +2,6 @@ use super::Location; use crate::error::PrinterError; -use crate::printer::Printer; use crate::traits::ToCss; use crate::values::ident::Ident; use crate::values::string::CSSString; @@ -30,11 +29,7 @@ pub struct NamespaceRule<'i> { } impl<'i> ToCss for NamespaceRule<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - #[cfg(feature = "sourcemap")] + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { dest.add_mapping(self.loc); dest.write_str("@namespace ")?; if let Some(prefix) = &self.prefix { @@ -43,6 +38,7 @@ impl<'i> ToCss for NamespaceRule<'i> { } self.url.to_css(dest)?; - dest.write_char(';') + dest.write_char(';')?; + Ok(()) } } diff --git a/src/rules/nesting.rs b/src/rules/nesting.rs index 679e586aa..705ab27d4 100644 --- a/src/rules/nesting.rs +++ b/src/rules/nesting.rs @@ -9,7 +9,6 @@ use crate::context::DeclarationContext; use crate::declaration::DeclarationBlock; use crate::error::{MinifyError, PrinterError}; use crate::parser::DefaultAtRule; -use crate::printer::Printer; use crate::targets::should_compile; use crate::traits::ToCss; #[cfg(feature = "visitor")] @@ -41,13 +40,9 @@ impl<'i, T: Clone> NestingRule<'i, T> { } impl<'a, 'i, T: ToCss> ToCss for NestingRule<'i, T> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - #[cfg(feature = "sourcemap")] + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { dest.add_mapping(self.loc); - if dest.context().is_none() { + if dest.state().context.is_none() { dest.write_str("@nest ")?; } self.style.to_css(dest) @@ -85,18 +80,20 @@ impl<'i> NestedDeclarationsRule<'i> { } impl<'i> ToCss for NestedDeclarationsRule<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - #[cfg(feature = "sourcemap")] + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { dest.add_mapping(self.loc); - if should_compile!(dest.targets.current, Nesting) { - if let Some(context) = dest.context() { + if should_compile!(dest.state().targets.current, Nesting) { + let context = crate::rules::detach_style_context(dest.state().context); + if let Some(context) = context { let has_printable_declarations = self.declarations.has_printable_declarations(); if has_printable_declarations { - dest.with_parent_context(|dest| context.selectors.to_css(dest))?; + let parent = dest.state().context; + dest.state_mut().context = None; + dest.state_mut().context = context.parent.map(crate::rules::style_context_ptr); + let res = context.selectors.to_css(dest); + dest.state_mut().context = parent; + res?; dest.whitespace()?; dest.write_char('{')?; dest.indent(); diff --git a/src/rules/page.rs b/src/rules/page.rs index 760476fa2..e2b09a1cb 100644 --- a/src/rules/page.rs +++ b/src/rules/page.rs @@ -4,7 +4,6 @@ use super::Location; use crate::declaration::{parse_declaration, DeclarationBlock}; use crate::error::{ParserError, PrinterError}; use crate::macros::enum_property; -use crate::printer::Printer; use crate::stylesheet::ParserOptions; use crate::traits::{Parse, ToCss}; use crate::values::string::CowArcStr; @@ -141,11 +140,7 @@ pub struct PageMarginRule<'i> { } impl<'i> ToCss for PageMarginRule<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - #[cfg(feature = "sourcemap")] + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { dest.add_mapping(self.loc); dest.write_char('@')?; self.margin_box.to_css(dest)?; @@ -209,16 +204,12 @@ impl<'i> PageRule<'i> { } impl<'i> ToCss for PageRule<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - #[cfg(feature = "sourcemap")] + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { dest.add_mapping(self.loc); dest.write_str("@page")?; if let Some(first) = self.selectors.first() { // Space is only required if the first selector has a name. - if !dest.minify || first.name.is_some() { + if !dest.options().minify || first.name.is_some() { dest.write_char(' ')?; } let mut first = true; @@ -244,7 +235,7 @@ impl<'i> ToCss for PageRule<'i> { for decl in &$decls { dest.newline()?; decl.to_css(dest, $important)?; - if i != len - 1 || !dest.minify { + if i != len - 1 || !dest.options().minify { dest.write_char(';')?; } i += 1; @@ -256,7 +247,7 @@ impl<'i> ToCss for PageRule<'i> { write!(self.declarations.important_declarations, true); if !self.rules.is_empty() { - if !dest.minify && self.declarations.len() > 0 { + if !dest.options().minify && self.declarations.len() > 0 { dest.write_char('\n')?; } dest.newline()?; @@ -266,7 +257,7 @@ impl<'i> ToCss for PageRule<'i> { if first { first = false; } else { - if !dest.minify { + if !dest.options().minify { dest.write_char('\n')?; } dest.newline()?; @@ -277,15 +268,13 @@ impl<'i> ToCss for PageRule<'i> { dest.dedent(); dest.newline()?; - dest.write_char('}') + dest.write_char('}')?; + Ok(()) } } impl<'i> ToCss for PageSelector<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { if let Some(name) = &self.name { dest.write_str(&name)?; } diff --git a/src/rules/position_try.rs b/src/rules/position_try.rs index a051c7463..4f55703a9 100644 --- a/src/rules/position_try.rs +++ b/src/rules/position_try.rs @@ -3,7 +3,6 @@ use super::Location; use crate::declaration::DeclarationBlock; use crate::error::PrinterError; -use crate::printer::Printer; use crate::traits::ToCss; use crate::values::ident::DashedIdent; #[cfg(feature = "visitor")] @@ -27,11 +26,7 @@ pub struct PositionTryRule<'i> { } impl<'i> ToCss for PositionTryRule<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - #[cfg(feature = "sourcemap")] + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { dest.add_mapping(self.loc); dest.write_str("@position-try ")?; self.name.to_css(dest)?; diff --git a/src/rules/property.rs b/src/rules/property.rs index d70f91601..772d88540 100644 --- a/src/rules/property.rs +++ b/src/rules/property.rs @@ -5,7 +5,6 @@ use super::Location; use crate::visitor::Visit; use crate::{ error::{ParserError, PrinterError}, - printer::Printer, properties::custom::TokenList, traits::{Parse, ToCss}, values::{ @@ -110,11 +109,7 @@ impl<'i> PropertyRule<'i> { } impl<'i> ToCss for PropertyRule<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - #[cfg(feature = "sourcemap")] + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { dest.add_mapping(self.loc); dest.write_str("@property ")?; self.name.to_css(dest)?; @@ -144,14 +139,15 @@ impl<'i> ToCss for PropertyRule<'i> { dest.whitespace()?; initial_value.to_css(dest)?; - if !dest.minify { + if !dest.options().minify { dest.write_char(';')?; } } dest.dedent(); dest.newline()?; - dest.write_char('}') + dest.write_char('}')?; + Ok(()) } } diff --git a/src/rules/scope.rs b/src/rules/scope.rs index ab7d10048..41e7e60c9 100644 --- a/src/rules/scope.rs +++ b/src/rules/scope.rs @@ -1,10 +1,9 @@ //! The `@scope` rule. -use super::Location; +use super::{detach_style_context, style_context_ptr, Location, StyleContext}; use super::{CssRuleList, MinifyContext}; use crate::error::{MinifyError, PrinterError}; use crate::parser::DefaultAtRule; -use crate::printer::Printer; use crate::selector::{is_pure_css_modules_selector, SelectorList}; use crate::traits::ToCss; #[cfg(feature = "visitor")] @@ -64,11 +63,7 @@ impl<'i, T: Clone> ScopeRule<'i, T> { } impl<'i, T: ToCss> ToCss for ScopeRule<'i, T> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - #[cfg(feature = "sourcemap")] + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { dest.add_mapping(self.loc); dest.write_str("@scope")?; dest.whitespace()?; @@ -79,14 +74,23 @@ impl<'i, T: ToCss> ToCss for ScopeRule<'i, T> { dest.whitespace()?; } if let Some(scope_end) = &self.scope_end { - if dest.minify { + if dest.options().minify { dest.write_char(' ')?; } dest.write_str("to (")?; // is treated as an ancestor of scope end. // https://drafts.csswg.org/css-nesting/#nesting-at-scope if let Some(scope_start) = &self.scope_start { - dest.with_context(scope_start, |dest| scope_end.to_css(dest))?; + let parent = dest.state().context; + dest.state_mut().context = None; + let ctx = StyleContext { + selectors: unsafe { std::mem::transmute(scope_start) }, + parent: detach_style_context(parent), + }; + dest.state_mut().context = Some(style_context_ptr(&ctx)); + let res = scope_end.to_css(dest); + dest.state_mut().context = parent; + res?; } else { scope_end.to_css(dest)?; } @@ -99,9 +103,14 @@ impl<'i, T: ToCss> ToCss for ScopeRule<'i, T> { // Nested style rules within @scope are implicitly relative to the // so clear our style context while printing them to avoid replacing & ourselves. // https://drafts.csswg.org/css-cascade-6/#scoped-rules - dest.with_cleared_context(|dest| self.rules.to_css(dest))?; + let parent = dest.state().context; + dest.state_mut().context = None; + let res = self.rules.to_css(dest); + dest.state_mut().context = parent; + res?; dest.dedent(); dest.newline()?; - dest.write_char('}') + dest.write_char('}')?; + Ok(()) } } diff --git a/src/rules/starting_style.rs b/src/rules/starting_style.rs index 267156fc5..aa7658196 100644 --- a/src/rules/starting_style.rs +++ b/src/rules/starting_style.rs @@ -4,7 +4,6 @@ use super::Location; use super::{CssRuleList, MinifyContext}; use crate::error::{MinifyError, PrinterError}; use crate::parser::DefaultAtRule; -use crate::printer::Printer; use crate::traits::ToCss; #[cfg(feature = "visitor")] use crate::visitor::Visit; @@ -36,11 +35,7 @@ impl<'i, T: Clone> StartingStyleRule<'i, T> { } impl<'i, T: ToCss> ToCss for StartingStyleRule<'i, T> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - #[cfg(feature = "sourcemap")] + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { dest.add_mapping(self.loc); dest.write_str("@starting-style")?; dest.whitespace()?; @@ -50,6 +45,7 @@ impl<'i, T: ToCss> ToCss for StartingStyleRule<'i, T> { self.rules.to_css(dest)?; dest.dedent(); dest.newline()?; - dest.write_char('}') + dest.write_char('}')?; + Ok(()) } } diff --git a/src/rules/style.rs b/src/rules/style.rs index 4e8cbe4a6..ef281fe58 100644 --- a/src/rules/style.rs +++ b/src/rules/style.rs @@ -10,8 +10,7 @@ use crate::declaration::DeclarationBlock; use crate::error::ParserError; use crate::error::{MinifyError, PrinterError}; use crate::parser::DefaultAtRule; -use crate::printer::Printer; -use crate::rules::{CssRule, CssRuleList}; +use crate::rules::{detach_style_context, style_context_ptr, CssRule, CssRuleList, StyleContext}; use crate::selector::{ downlevel_selectors, get_prefix, is_compatible, is_pure_css_modules_selector, is_unused, SelectorList, }; @@ -211,10 +210,7 @@ where } impl<'a, 'i, T: ToCss> ToCss for StyleRule<'i, T> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { if self.vendor_prefix.is_empty() { self.to_css_base(dest) } else { @@ -223,28 +219,26 @@ impl<'a, 'i, T: ToCss> ToCss for StyleRule<'i, T> { if first_rule { first_rule = false; } else { - if !dest.minify { + if !dest.options().minify { dest.write_char('\n')?; // no indent } dest.newline()?; } - dest.vendor_prefix = prefix; - self.to_css_base(dest)?; + let old = dest.state().vendor_prefix; + dest.state_mut().vendor_prefix = prefix; + let res = self.to_css_base(dest); + dest.state_mut().vendor_prefix = old; + res?; } - - dest.vendor_prefix = VendorPrefix::empty(); Ok(()) } } } impl<'a, 'i, T: ToCss> StyleRule<'i, T> { - fn to_css_base(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css_base(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { // If supported, or there are no targets, preserve nesting. Otherwise, write nested rules after parent. - let supports_nesting = self.rules.0.is_empty() || !should_compile!(dest.targets.current, Nesting); + let supports_nesting = self.rules.0.is_empty() || !should_compile!(dest.state().targets.current, Nesting); let must_preserve_rule = |rule: &CssRule<'i, T>| matches!(rule, CssRule::Unknown(_)); let has_preserved_rules = !supports_nesting && self.rules.0.iter().any(must_preserve_rule); let has_hoisted_rules = !supports_nesting && self.rules.0.iter().any(|rule| !must_preserve_rule(rule)); @@ -252,7 +246,6 @@ impl<'a, 'i, T: ToCss> StyleRule<'i, T> { let has_declarations = supports_nesting || len > 0 || self.rules.0.is_empty() || has_preserved_rules; if has_declarations { - #[cfg(feature = "sourcemap")] dest.add_mapping(self.loc); self.selectors.to_css(dest)?; dest.whitespace()?; @@ -272,7 +265,10 @@ impl<'a, 'i, T: ToCss> StyleRule<'i, T> { macro_rules! newline { () => { - if !dest.minify && (supports_nesting || len > 0 || has_preserved_rules) && !self.rules.0.is_empty() { + if !dest.options().minify + && (supports_nesting || len > 0 || has_preserved_rules) + && !self.rules.0.is_empty() + { if len > 0 || has_preserved_rules { dest.write_char('\n')?; } @@ -299,7 +295,7 @@ impl<'a, 'i, T: ToCss> StyleRule<'i, T> { } else { let mut first = true; for rule in self.rules.0.iter().filter(|rule| must_preserve_rule(rule)) { - if !dest.minify || first { + if !dest.options().minify || first { dest.newline()?; } rule.to_css(dest)?; @@ -309,13 +305,20 @@ impl<'a, 'i, T: ToCss> StyleRule<'i, T> { if has_hoisted_rules { newline!(); - dest.with_context(&self.selectors, |dest| { + let parent = dest.state().context; + dest.state_mut().context = None; + let ctx = StyleContext { + selectors: unsafe { std::mem::transmute(&self.selectors) }, + parent: detach_style_context(parent), + }; + dest.state_mut().context = Some(style_context_ptr(&ctx)); + let res = { let mut first = true; for rule in self.rules.0.iter().filter(|rule| !must_preserve_rule(rule)) { if first { first = false; } else { - if !dest.minify { + if !dest.options().minify { dest.write_char('\n')?; } dest.newline()?; @@ -324,7 +327,9 @@ impl<'a, 'i, T: ToCss> StyleRule<'i, T> { } Ok::<(), PrinterError>(()) - })?; + }; + dest.state_mut().context = parent; + res?; } } diff --git a/src/rules/supports.rs b/src/rules/supports.rs index a8deccd1b..a16a8fc1b 100644 --- a/src/rules/supports.rs +++ b/src/rules/supports.rs @@ -6,7 +6,6 @@ use super::Location; use super::{CssRuleList, MinifyContext}; use crate::error::{MinifyError, ParserError, PrinterError}; use crate::parser::DefaultAtRule; -use crate::printer::Printer; use crate::properties::custom::TokenList; use crate::properties::PropertyId; use crate::targets::{Features, FeaturesIterator, Targets}; @@ -60,11 +59,7 @@ impl<'i, T: Clone> SupportsRule<'i, T> { } impl<'a, 'i, T: ToCss> ToCss for SupportsRule<'i, T> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - #[cfg(feature = "sourcemap")] + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { dest.add_mapping(self.loc); dest.write_str("@supports ")?; self.condition.to_css(dest)?; @@ -73,15 +68,16 @@ impl<'a, 'i, T: ToCss> ToCss for SupportsRule<'i, T> { dest.indent(); dest.newline()?; - let inserted = dest.targets.enter_supports(self.condition.get_supported_features()); + let inserted = dest.state_mut().targets.enter_supports(self.condition.get_supported_features()); self.rules.to_css(dest)?; if inserted { - dest.targets.exit_supports(); + dest.state_mut().targets.exit_supports(); } dest.dedent(); dest.newline()?; - dest.write_char('}') + dest.write_char('}')?; + Ok(()) } } @@ -337,10 +333,11 @@ impl<'i> SupportsCondition<'i> { } } - fn to_css_with_parens_if_needed(&self, dest: &mut Printer, needs_parens: bool) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css_with_parens_if_needed( + &self, + dest: &mut PrinterT, + needs_parens: bool, + ) -> Result<(), PrinterError> { if needs_parens { dest.write_char('(')?; } @@ -353,10 +350,7 @@ impl<'i> SupportsCondition<'i> { } impl<'i> ToCss for SupportsCondition<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { SupportsCondition::Not(condition) => { dest.write_str("not ")?; @@ -413,14 +407,20 @@ impl<'i> ToCss for SupportsCondition<'i> { dest.write_char(')')?; } - dest.write_char(')') + dest.write_char(')')?; + + Ok(()) } SupportsCondition::Selector(sel) => { dest.write_str("selector(")?; dest.write_str(sel)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) + } + SupportsCondition::Unknown(unknown) => { + dest.write_str(&unknown)?; + Ok(()) } - SupportsCondition::Unknown(unknown) => dest.write_str(&unknown), } } } diff --git a/src/rules/unknown.rs b/src/rules/unknown.rs index a3a2263fb..5725b6733 100644 --- a/src/rules/unknown.rs +++ b/src/rules/unknown.rs @@ -2,7 +2,6 @@ use super::Location; use crate::error::PrinterError; -use crate::printer::Printer; use crate::properties::custom::TokenList; use crate::traits::ToCss; use crate::values::string::CowArcStr; @@ -30,11 +29,7 @@ pub struct UnknownAtRule<'i> { } impl<'i> ToCss for UnknownAtRule<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - #[cfg(feature = "sourcemap")] + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { dest.add_mapping(self.loc); dest.write_char('@')?; dest.write_str(&self.name)?; @@ -52,9 +47,11 @@ impl<'i> ToCss for UnknownAtRule<'i> { block.to_css(dest, false)?; dest.dedent(); dest.newline()?; - dest.write_char('}') + dest.write_char('}')?; + Ok(()) } else { - dest.write_char(';') + dest.write_char(';')?; + Ok(()) } } } diff --git a/src/rules/view_transition.rs b/src/rules/view_transition.rs index 3c9753c94..5578bb07d 100644 --- a/src/rules/view_transition.rs +++ b/src/rules/view_transition.rs @@ -2,7 +2,6 @@ use super::Location; use crate::error::{ParserError, PrinterError}; -use crate::printer::Printer; use crate::properties::custom::CustomProperty; use crate::stylesheet::ParserOptions; use crate::traits::{Parse, ToCss}; @@ -147,11 +146,7 @@ impl<'i> ViewTransitionRule<'i> { } impl<'i> ToCss for ViewTransitionRule<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - #[cfg(feature = "sourcemap")] + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { dest.add_mapping(self.loc); dest.write_str("@view-transition")?; dest.whitespace()?; @@ -161,21 +156,19 @@ impl<'i> ToCss for ViewTransitionRule<'i> { for (i, prop) in self.properties.iter().enumerate() { dest.newline()?; prop.to_css(dest)?; - if i != len - 1 || !dest.minify { + if i != len - 1 || !dest.options().minify { dest.write_char(';')?; } } dest.dedent(); dest.newline()?; - dest.write_char('}') + dest.write_char('}')?; + Ok(()) } } impl<'i> ToCss for ViewTransitionProperty<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { macro_rules! property { ($prop: literal, $value: expr) => {{ dest.write_str($prop)?; diff --git a/src/rules/viewport.rs b/src/rules/viewport.rs index eca6d1446..1b063ad81 100644 --- a/src/rules/viewport.rs +++ b/src/rules/viewport.rs @@ -3,7 +3,6 @@ use super::Location; use crate::declaration::DeclarationBlock; use crate::error::PrinterError; -use crate::printer::Printer; use crate::traits::ToCss; use crate::vendor_prefix::VendorPrefix; #[cfg(feature = "visitor")] @@ -32,11 +31,7 @@ pub struct ViewportRule<'i> { } impl<'i> ToCss for ViewportRule<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - #[cfg(feature = "sourcemap")] + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { dest.add_mapping(self.loc); dest.write_char('@')?; self.vendor_prefix.to_css(dest)?; diff --git a/src/selector.rs b/src/selector.rs index 95f15655f..c2d4d5c54 100644 --- a/src/selector.rs +++ b/src/selector.rs @@ -5,7 +5,7 @@ use crate::error::{ParserError, PrinterError, SelectorError}; use crate::parser::ParserFlags; use crate::printer::Printer; use crate::properties::custom::TokenList; -use crate::rules::StyleContext; +use crate::rules::{detach_style_context, StyleContext}; use crate::stylesheet::{ParserOptions, PrinterOptions}; use crate::targets::{should_compile, Targets}; use crate::traits::{Parse, ParseWithOptions, ToCss}; @@ -668,20 +668,17 @@ impl<'i> cssparser::ToCss for PseudoClass<'i> { W: fmt::Write, { let mut s = String::new(); - serialize_pseudo_class(self, &mut Printer::new(&mut s, Default::default()), None) - .map_err(|_| std::fmt::Error)?; + let mut printer = Printer::new(&mut s, PrinterOptions::default()); + serialize_pseudo_class(self, &mut printer, None).map_err(|_| std::fmt::Error)?; write!(dest, "{}", s) } } -fn serialize_pseudo_class<'a, 'i, W>( +fn serialize_pseudo_class<'a, 'i, PrinterT: crate::printer::PrinterTrait>( pseudo_class: &PseudoClass<'i>, - dest: &mut Printer, + dest: &mut PrinterT, context: Option<&StyleContext>, -) -> Result<(), PrinterError> -where - W: fmt::Write, -{ +) -> Result<(), PrinterError> { use PseudoClass::*; match pseudo_class { Lang { languages: lang } => { @@ -695,17 +692,17 @@ where } serialize_identifier(lang, dest)?; } - return dest.write_str(")"); + return Ok(dest.write_str(")")?); } Dir { direction: dir } => { dest.write_str(":dir(")?; dir.to_css(dest)?; - return dest.write_str(")"); + return Ok(dest.write_str(")")?); } State { state } => { dest.write_str(":state(")?; state.to_css(dest)?; - return dest.write_str(")"); + return Ok(dest.write_str(")")?); } _ => {} } @@ -714,29 +711,32 @@ where ($prefix: ident, $val: expr) => {{ dest.write_char(':')?; // If the printer has a vendor prefix override, use that. - let vp = if !dest.vendor_prefix.is_empty() { - (dest.vendor_prefix & *$prefix).or_none() + let vp = if !dest.state().vendor_prefix.is_empty() { + (dest.state().vendor_prefix & *$prefix).or_none() } else { *$prefix }; vp.to_css(dest)?; - dest.write_str($val) + dest.write_str($val)?; + Ok(()) }}; } macro_rules! pseudo { ($key: ident, $s: literal) => {{ - let class = if let Some(pseudo_classes) = &dest.pseudo_classes { - pseudo_classes.$key - } else { - None - }; + let class = dest + .options() + .pseudo_classes + .as_ref() + .and_then(|pseudo_classes| pseudo_classes.$key) + .map(|class| unsafe { std::mem::transmute::<&str, &'static str>(class) }); if let Some(class) = class { dest.write_char('.')?; dest.write_ident(class, true) } else { - dest.write_str($s) + dest.write_str($s)?; + Ok(()) } }}; } @@ -750,97 +750,212 @@ where FocusWithin => pseudo!(focus_within, ":focus-within"), // https://drafts.csswg.org/selectors-4/#time-pseudos - Current => dest.write_str(":current"), - Past => dest.write_str(":past"), - Future => dest.write_str(":future"), + Current => { + dest.write_str(":current")?; + Ok(()) + } + Past => { + dest.write_str(":past")?; + Ok(()) + } + Future => { + dest.write_str(":future")?; + Ok(()) + } // https://drafts.csswg.org/selectors-4/#resource-pseudos - Playing => dest.write_str(":playing"), - Paused => dest.write_str(":paused"), - Seeking => dest.write_str(":seeking"), - Buffering => dest.write_str(":buffering"), - Stalled => dest.write_str(":stalled"), - Muted => dest.write_str(":muted"), - VolumeLocked => dest.write_str(":volume-locked"), + Playing => { + dest.write_str(":playing")?; + Ok(()) + } + Paused => { + dest.write_str(":paused")?; + Ok(()) + } + Seeking => { + dest.write_str(":seeking")?; + Ok(()) + } + Buffering => { + dest.write_str(":buffering")?; + Ok(()) + } + Stalled => { + dest.write_str(":stalled")?; + Ok(()) + } + Muted => { + dest.write_str(":muted")?; + Ok(()) + } + VolumeLocked => { + dest.write_str(":volume-locked")?; + Ok(()) + } // https://fullscreen.spec.whatwg.org/#:fullscreen-pseudo-class Fullscreen(prefix) => { dest.write_char(':')?; - let vp = if !dest.vendor_prefix.is_empty() { - (dest.vendor_prefix & *prefix).or_none() + let vp = if !dest.state().vendor_prefix.is_empty() { + (dest.state().vendor_prefix & *prefix).or_none() } else { *prefix }; vp.to_css(dest)?; if vp == VendorPrefix::WebKit || vp == VendorPrefix::Moz { - dest.write_str("full-screen") + dest.write_str("full-screen")?; + Ok(()) } else { - dest.write_str("fullscreen") + dest.write_str("fullscreen")?; + Ok(()) } } // https://drafts.csswg.org/selectors/#display-state-pseudos - Open => dest.write_str(":open"), - Closed => dest.write_str(":closed"), - Modal => dest.write_str(":modal"), - PictureInPicture => dest.write_str(":picture-in-picture"), + Open => { + dest.write_str(":open")?; + Ok(()) + } + Closed => { + dest.write_str(":closed")?; + Ok(()) + } + Modal => { + dest.write_str(":modal")?; + Ok(()) + } + PictureInPicture => { + dest.write_str(":picture-in-picture")?; + Ok(()) + } // https://html.spec.whatwg.org/multipage/semantics-other.html#selector-popover-open - PopoverOpen => dest.write_str(":popover-open"), + PopoverOpen => { + dest.write_str(":popover-open")?; + Ok(()) + } // https://drafts.csswg.org/selectors-4/#the-defined-pseudo - Defined => dest.write_str(":defined"), + Defined => { + dest.write_str(":defined")?; + Ok(()) + } // https://drafts.csswg.org/selectors-4/#location AnyLink(prefix) => write_prefixed!(prefix, "any-link"), - Link => dest.write_str(":link"), - LocalLink => dest.write_str(":local-link"), - Target => dest.write_str(":target"), - TargetWithin => dest.write_str(":target-within"), + Link => { + dest.write_str(":link")?; + Ok(()) + } + LocalLink => { + dest.write_str(":local-link")?; + Ok(()) + } + Target => { + dest.write_str(":target")?; + Ok(()) + } + TargetWithin => { + dest.write_str(":target-within")?; + Ok(()) + } // https://drafts.csswg.org/css-overflow-5/#active-before-after-scroll-markers - TargetCurrent => dest.write_str(":target-current"), - TargetBefore => dest.write_str(":target-before"), - TargetAfter => dest.write_str(":target-after"), + TargetCurrent => { + dest.write_str(":target-current")?; + Ok(()) + } + TargetBefore => { + dest.write_str(":target-before")?; + Ok(()) + } + TargetAfter => { + dest.write_str(":target-after")?; + Ok(()) + } - Visited => dest.write_str(":visited"), + Visited => { + dest.write_str(":visited")?; + Ok(()) + } // https://drafts.csswg.org/selectors-4/#input-pseudos - Enabled => dest.write_str(":enabled"), - Disabled => dest.write_str(":disabled"), + Enabled => { + dest.write_str(":enabled")?; + Ok(()) + } + Disabled => { + dest.write_str(":disabled")?; + Ok(()) + } ReadOnly(prefix) => write_prefixed!(prefix, "read-only"), ReadWrite(prefix) => write_prefixed!(prefix, "read-write"), PlaceholderShown(prefix) => write_prefixed!(prefix, "placeholder-shown"), - Default => dest.write_str(":default"), - Checked => dest.write_str(":checked"), - Indeterminate => dest.write_str(":indeterminate"), - Blank => dest.write_str(":blank"), - Valid => dest.write_str(":valid"), - Invalid => dest.write_str(":invalid"), - InRange => dest.write_str(":in-range"), - OutOfRange => dest.write_str(":out-of-range"), - Required => dest.write_str(":required"), - Optional => dest.write_str(":optional"), - UserValid => dest.write_str(":user-valid"), - UserInvalid => dest.write_str(":user-invalid"), + Default => { + dest.write_str(":default")?; + Ok(()) + } + Checked => { + dest.write_str(":checked")?; + Ok(()) + } + Indeterminate => { + dest.write_str(":indeterminate")?; + Ok(()) + } + Blank => { + dest.write_str(":blank")?; + Ok(()) + } + Valid => { + dest.write_str(":valid")?; + Ok(()) + } + Invalid => { + dest.write_str(":invalid")?; + Ok(()) + } + InRange => { + dest.write_str(":in-range")?; + Ok(()) + } + OutOfRange => { + dest.write_str(":out-of-range")?; + Ok(()) + } + Required => { + dest.write_str(":required")?; + Ok(()) + } + Optional => { + dest.write_str(":optional")?; + Ok(()) + } + UserValid => { + dest.write_str(":user-valid")?; + Ok(()) + } + UserInvalid => { + dest.write_str(":user-invalid")?; + Ok(()) + } // https://html.spec.whatwg.org/multipage/semantics-other.html#selector-autofill Autofill(prefix) => write_prefixed!(prefix, "autofill"), - ActiveViewTransition => dest.write_str(":active-view-transition"), + ActiveViewTransition => { + dest.write_str(":active-view-transition")?; + Ok(()) + } ActiveViewTransitionType { kind } => { dest.write_str(":active-view-transition-type(")?; kind.to_css(dest)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } Local { selector } => serialize_selector(selector, dest, context, false), - Global { selector } => { - let css_module = std::mem::take(&mut dest.css_module); - serialize_selector(selector, dest, context, false)?; - dest.css_module = css_module; - Ok(()) - } + Global { selector } => dest.without_css_module(|dest| serialize_selector(selector, dest, context, false)), // https://webkit.org/blog/363/styling-scrollbars/ WebKitScrollbar(s) => { @@ -857,20 +972,22 @@ where NoButton => ":no-button", CornerPresent => ":corner-present", WindowInactive => ":window-inactive", - }) + })?; + Ok(()) } Lang { languages: _ } | Dir { direction: _ } | State { .. } => unreachable!(), Custom { name } => { dest.write_char(':')?; - return dest.write_str(&name); + return Ok(dest.write_str(&name)?); } CustomFunction { name, arguments: args } => { dest.write_char(':')?; dest.write_str(name)?; dest.write_char('(')?; args.to_css_raw(dest)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } } } @@ -1098,12 +1215,12 @@ impl<'i> Parse<'i> for ViewTransitionPartName<'i> { } impl<'i> ToCss for ViewTransitionPartName<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { - ViewTransitionPartName::All => dest.write_char('*'), + ViewTransitionPartName::All => { + dest.write_char('*')?; + Ok(()) + } ViewTransitionPartName::Name(name) => name.to_css(dest), } } @@ -1147,10 +1264,7 @@ impl<'i> Parse<'i> for ViewTransitionPartSelector<'i> { } impl<'i> ToCss for ViewTransitionPartSelector<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { if let Some(name) = &self.name { name.to_css(dest)?; } @@ -1168,28 +1282,25 @@ impl<'i> cssparser::ToCss for PseudoElement<'i> { W: fmt::Write, { let mut s = String::new(); - serialize_pseudo_element(self, &mut Printer::new(&mut s, Default::default()), None) - .map_err(|_| std::fmt::Error)?; + let mut printer = Printer::new(&mut s, PrinterOptions::default()); + serialize_pseudo_element(self, &mut printer, None).map_err(|_| std::fmt::Error)?; write!(dest, "{}", s) } } -fn serialize_pseudo_element<'a, 'i, W>( +fn serialize_pseudo_element<'a, 'i, PrinterT: crate::printer::PrinterTrait>( pseudo_element: &PseudoElement, - dest: &mut Printer, + dest: &mut PrinterT, context: Option<&StyleContext>, -) -> Result<(), PrinterError> -where - W: fmt::Write, -{ +) -> Result<(), PrinterError> { use PseudoElement::*; macro_rules! write_prefix { ($prefix: ident) => {{ dest.write_str("::")?; // If the printer has a vendor prefix override, use that. - let vp = if !dest.vendor_prefix.is_empty() { - (dest.vendor_prefix & *$prefix).or_none() + let vp = if !dest.state().vendor_prefix.is_empty() { + (dest.state().vendor_prefix & *$prefix).or_none() } else { *$prefix }; @@ -1201,7 +1312,8 @@ where macro_rules! write_prefixed { ($prefix: ident, $val: expr) => {{ write_prefix!($prefix); - dest.write_str($val) + dest.write_str($val)?; + Ok(()) }}; } @@ -1209,49 +1321,87 @@ where // CSS2 pseudo elements support a single colon syntax in addition // to the more correct double colon for other pseudo elements. // We use that here because it's supported everywhere and is shorter. - After => dest.write_str(":after"), - Before => dest.write_str(":before"), - FirstLine => dest.write_str(":first-line"), - FirstLetter => dest.write_str(":first-letter"), - DetailsContent => dest.write_str("::details-content"), - TargetText => dest.write_str("::target-text"), - SearchText => dest.write_str("::search-text"), + After => { + dest.write_str(":after")?; + Ok(()) + } + Before => { + dest.write_str(":before")?; + Ok(()) + } + FirstLine => { + dest.write_str(":first-line")?; + Ok(()) + } + FirstLetter => { + dest.write_str(":first-letter")?; + Ok(()) + } + DetailsContent => { + dest.write_str("::details-content")?; + Ok(()) + } + TargetText => { + dest.write_str("::target-text")?; + Ok(()) + } + SearchText => { + dest.write_str("::search-text")?; + Ok(()) + } HighlightFunction { name } => { dest.write_str("::highlight(")?; name.to_css(dest)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) + } + Marker => { + dest.write_str("::marker")?; + Ok(()) } - Marker => dest.write_str("::marker"), Selection(prefix) => write_prefixed!(prefix, "selection"), - Cue => dest.write_str("::cue"), - CueRegion => dest.write_str("::cue-region"), + Cue => { + dest.write_str("::cue")?; + Ok(()) + } + CueRegion => { + dest.write_str("::cue-region")?; + Ok(()) + } CueFunction { selector } => { dest.write_str("::cue(")?; serialize_selector(selector, dest, context, false)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } CueRegionFunction { selector } => { dest.write_str("::cue-region(")?; serialize_selector(selector, dest, context, false)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } Placeholder(prefix) => { let vp = write_prefix!(prefix); if vp == VendorPrefix::WebKit || vp == VendorPrefix::Ms { - dest.write_str("input-placeholder") + dest.write_str("input-placeholder")?; + Ok(()) } else { - dest.write_str("placeholder") + dest.write_str("placeholder")?; + Ok(()) } } Backdrop(prefix) => write_prefixed!(prefix, "backdrop"), FileSelectorButton(prefix) => { let vp = write_prefix!(prefix); if vp == VendorPrefix::WebKit { - dest.write_str("file-upload-button") + dest.write_str("file-upload-button")?; + Ok(()) } else if vp == VendorPrefix::Ms { - dest.write_str("browse") + dest.write_str("browse")?; + Ok(()) } else { - dest.write_str("file-selector-button") + dest.write_str("file-selector-button")?; + Ok(()) } } WebKitScrollbar(s) => { @@ -1264,48 +1414,70 @@ where Thumb => "::-webkit-scrollbar-thumb", Corner => "::-webkit-scrollbar-corner", Resizer => "::-webkit-resizer", - }) + })?; + Ok(()) + } + ViewTransition => { + dest.write_str("::view-transition")?; + Ok(()) } - ViewTransition => dest.write_str("::view-transition"), ViewTransitionGroup { part } => { dest.write_str("::view-transition-group(")?; part.to_css(dest)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } ViewTransitionImagePair { part } => { dest.write_str("::view-transition-image-pair(")?; part.to_css(dest)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } ViewTransitionOld { part } => { dest.write_str("::view-transition-old(")?; part.to_css(dest)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } ViewTransitionNew { part } => { dest.write_str("::view-transition-new(")?; part.to_css(dest)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } PickerFunction { identifier } => { dest.write_str("::picker(")?; identifier.to_css(dest)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) + } + PickerIcon => { + dest.write_str("::picker-icon")?; + Ok(()) + } + Checkmark => { + dest.write_str("::checkmark")?; + Ok(()) + } + GrammarError => { + dest.write_str("::grammar-error")?; + Ok(()) + } + SpellingError => { + dest.write_str("::spelling-error")?; + Ok(()) } - PickerIcon => dest.write_str("::picker-icon"), - Checkmark => dest.write_str("::checkmark"), - GrammarError => dest.write_str("::grammar-error"), - SpellingError => dest.write_str("::spelling-error"), Custom { name: val } => { dest.write_str("::")?; - return dest.write_str(val); + return Ok(dest.write_str(val)?); } CustomFunction { name, arguments: args } => { dest.write_str("::")?; dest.write_str(name)?; dest.write_char('(')?; args.to_css_raw(dest)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } } } @@ -1391,25 +1563,26 @@ impl<'i> PseudoElement<'i> { } impl<'a, 'i> ToCss for SelectorList<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: fmt::Write, - { - serialize_selector_list(self.0.iter(), dest, dest.context(), false) + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { + let context = detach_style_context(dest.state().context); + serialize_selector_list(self.0.iter(), dest, context, false) } } impl ToCss for Combinator { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match *self { Combinator::Child => dest.delim('>', true), - Combinator::Descendant => dest.write_str(" "), + Combinator::Descendant => { + dest.write_str(" ")?; + Ok(()) + } Combinator::NextSibling => dest.delim('+', true), Combinator::LaterSibling => dest.delim('~', true), - Combinator::Deep => dest.write_str(" /deep/ "), + Combinator::Deep => { + dest.write_str(" /deep/ ")?; + Ok(()) + } Combinator::DeepDescendant => { dest.whitespace()?; dest.write_str(">>>")?; @@ -1422,23 +1595,18 @@ impl ToCss for Combinator { // Copied from the selectors crate and modified to override to_css implementation. impl<'a, 'i> ToCss for Selector<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: fmt::Write, - { - serialize_selector(self, dest, dest.context(), false) + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { + let context = detach_style_context(dest.state().context); + serialize_selector(self, dest, context, false) } } -fn serialize_selector<'a, 'i, W>( +fn serialize_selector<'a, 'i, PrinterT: crate::printer::PrinterTrait>( selector: &Selector<'i>, - dest: &mut Printer, + dest: &mut PrinterT, context: Option<&StyleContext>, mut is_relative: bool, -) -> Result<(), PrinterError> -where - W: fmt::Write, -{ +) -> Result<(), PrinterError> { use parcel_selectors::parser::*; // Compound selectors invert the order of their contents, so we need to // undo that during serialization. @@ -1454,7 +1622,7 @@ where let mut combinators = selector.iter_raw_match_order().rev().filter_map(|x| x.as_combinator()); let compound_selectors = selector.iter_raw_match_order().as_slice().split(|x| x.is_combinator()).rev(); - let should_compile_nesting = should_compile!(dest.targets.current, Nesting); + let should_compile_nesting = should_compile!(dest.state().targets.current, Nesting); let mut first = true; let mut combinators_exhausted = false; @@ -1598,14 +1766,11 @@ where Ok(()) } -fn serialize_component<'a, 'i, W>( +fn serialize_component<'a, 'i, PrinterT: crate::printer::PrinterTrait>( component: &Component, - dest: &mut Printer, + dest: &mut PrinterT, context: Option<&StyleContext>, -) -> Result<(), PrinterError> -where - W: fmt::Write, -{ +) -> Result<(), PrinterError> { match component { Component::Combinator(ref c) => c.to_css(dest), Component::AttributeInNoNamespace { @@ -1619,7 +1784,7 @@ where cssparser::ToCss::to_css(local_name, dest)?; cssparser::ToCss::to_css(operator, dest)?; - if dest.minify { + if dest.options().minify { // Serialize as both an identifier and a string and choose the shorter one. let mut id = String::new(); serialize_identifier(&value, &mut id)?; @@ -1641,7 +1806,8 @@ where parcel_selectors::attr::ParsedCaseSensitivity::AsciiCaseInsensitive => dest.write_str(" i")?, parcel_selectors::attr::ParsedCaseSensitivity::ExplicitCaseSensitive => dest.write_str(" s")?, } - dest.write_char(']') + dest.write_char(']')?; + Ok(()) } Component::Is(ref list) | Component::Where(ref list) @@ -1656,7 +1822,7 @@ where return Ok(()); } - let vp = dest.vendor_prefix; + let vp = dest.state().vendor_prefix; if vp.intersects(VendorPrefix::WebKit | VendorPrefix::Moz) { dest.write_char(':')?; vp.to_css(dest)?; @@ -1669,7 +1835,7 @@ where dest.write_str(":not(")?; } Component::Any(prefix, ..) => { - let vp = dest.vendor_prefix.or(prefix); + let vp = dest.state().vendor_prefix.or(prefix); if vp.intersects(VendorPrefix::WebKit | VendorPrefix::Moz) { dest.write_char(':')?; vp.to_css(dest)?; @@ -1681,12 +1847,14 @@ where _ => unreachable!(), } serialize_selector_list(list.iter(), dest, context, false)?; - dest.write_str(")") + dest.write_str(")")?; + Ok(()) } Component::Has(ref list) => { dest.write_str(":has(")?; serialize_selector_list(list.iter(), dest, context, true)?; - dest.write_str(")") + dest.write_str(")")?; + Ok(()) } Component::NonTSPseudoClass(pseudo) => serialize_pseudo_class(pseudo, dest, context), Component::PseudoElement(pseudo) => serialize_pseudo_element(pseudo, dest, context), @@ -1711,7 +1879,8 @@ where Component::Slotted(ref selector) => { dest.write_str("::slotted(")?; selector.to_css(dest)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } Component::NthOf(ref nth_of_data) => { let nth_data = nth_of_data.nth_data(); @@ -1719,7 +1888,8 @@ where nth_data.write_affine(dest)?; dest.write_str(" of ")?; serialize_selector_list(nth_of_data.selectors().iter(), dest, context, true)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } _ => { cssparser::ToCss::to_css(component, dest)?; @@ -1739,14 +1909,11 @@ fn should_unwrap_is<'i>(selectors: &Box<[Selector<'i>]>) -> bool { false } -fn serialize_nesting( - dest: &mut Printer, +fn serialize_nesting( + dest: &mut PrinterT, context: Option<&StyleContext>, first: bool, -) -> Result<(), PrinterError> -where - W: fmt::Write, -{ +) -> Result<(), PrinterError> { if let Some(ctx) = context { // If there's only one simple selector, just serialize it directly. // Otherwise, use an :is() pseudo class. @@ -1759,15 +1926,18 @@ where } else { dest.write_str(":is(")?; serialize_selector_list(ctx.selectors.0.iter(), dest, ctx.parent, false)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } } else { // If there is no context, we are at the root if nesting is supported. This is equivalent to :scope. // Otherwise, if nesting is supported, serialize the nesting selector directly. - if should_compile!(dest.targets.current, Nesting) { - dest.write_str(":scope") + if should_compile!(dest.state().targets.current, Nesting) { + dest.write_str(":scope")?; + Ok(()) } else { - dest.write_char('&') + dest.write_char('&')?; + Ok(()) } } } @@ -1812,15 +1982,14 @@ fn is_namespace(component: Option<&Component>) -> bool { ) } -fn serialize_selector_list<'a, 'i: 'a, I, W>( +fn serialize_selector_list<'a, 'i: 'a, I, PrinterT: crate::printer::PrinterTrait>( iter: I, - dest: &mut Printer, + dest: &mut PrinterT, context: Option<&StyleContext>, is_relative: bool, ) -> Result<(), PrinterError> where I: Iterator>, - W: fmt::Write, { let mut first = true; for selector in iter { diff --git a/src/stylesheet.rs b/src/stylesheet.rs index 65784708e..c21fd73d1 100644 --- a/src/stylesheet.rs +++ b/src/stylesheet.rs @@ -9,7 +9,9 @@ use crate::declaration::{DeclarationBlock, DeclarationHandler}; use crate::dependencies::Dependency; use crate::error::{Error, ErrorLocation, MinifyErrorKind, ParserError, PrinterError, PrinterErrorKind}; use crate::parser::{DefaultAtRule, DefaultAtRuleParser, TopLevelRuleParser}; -use crate::printer::Printer; +#[cfg(feature = "custom_sourcemap")] +use crate::printer::SourceMap; +use crate::printer::{PrinterTrait, Printer}; use crate::rules::{CssRule, CssRuleList, MinifyContext}; use crate::targets::{should_compile, Targets, TargetsWithSupportsScope}; use crate::traits::{AtRuleParser, ToCss}; @@ -17,8 +19,6 @@ use crate::values::string::CowArcStr; #[cfg(feature = "visitor")] use crate::visitor::{Visit, VisitTypes, Visitor}; use cssparser::{Parser, ParserInput, StyleSheetParser}; -#[cfg(feature = "sourcemap")] -use parcel_sourcemap::SourceMap; use std::collections::{HashMap, HashSet}; pub use crate::parser::{ParserFlags, ParserOptions}; @@ -37,7 +37,6 @@ pub use crate::printer::PseudoClasses; /// use rspack_lightningcss::stylesheet::{ /// StyleSheet, ParserOptions, MinifyOptions, PrinterOptions /// }; -/// /// // Parse a style sheet from a string. /// let mut stylesheet = StyleSheet::parse( /// r#" @@ -56,7 +55,7 @@ pub use crate::printer::PseudoClasses; /// stylesheet.minify(MinifyOptions::default()).unwrap(); /// /// // Serialize it to a string. -/// let res = stylesheet.to_css(PrinterOptions::default()).unwrap(); +/// let res = stylesheet.to_css(PrinterOptions::default(), None::<&mut ()>).unwrap(); /// assert_eq!(res.code, ".foo, .bar {\n color: red;\n}\n"); /// ``` #[derive(Debug, Clone)] @@ -215,10 +214,9 @@ where } /// Returns the inline source map associated with the source at the given index. - #[cfg(feature = "sourcemap")] - #[cfg_attr(docsrs, doc(cfg(feature = "sourcemap")))] - pub fn source_map(&self, source_index: usize) -> Option { - SourceMap::from_data_url("/", self.source_map_url(source_index)?).ok() + #[cfg(feature = "custom_sourcemap")] + pub fn source_map(&self, source_index: usize) -> Option { + S::from_data_url("/", self.source_map_url(source_index)?) } /// Minify and transform the style sheet for the provided browser targets. @@ -266,22 +264,65 @@ where } /// Serialize the style sheet to a CSS string. - pub fn to_css(&self, options: PrinterOptions) -> Result> { + #[cfg(feature = "custom_sourcemap")] + pub fn to_css<'a, S: SourceMap>( + &self, + options: PrinterOptions<'a>, + source_map: Option<&'a mut S>, + ) -> Result> { + self.to_css_impl(options, source_map) + } + + /// Serialize the style sheet to a CSS string. + #[cfg(not(feature = "custom_sourcemap"))] + pub fn to_css<'a>(&self, options: PrinterOptions<'a>) -> Result> { + self.to_css_impl(options) + } + + fn to_css_impl<'a, #[cfg(feature = "custom_sourcemap")] S: SourceMap>( + &self, + options: PrinterOptions<'a>, + #[cfg(feature = "custom_sourcemap")] source_map: Option<&'a mut S>, + ) -> Result> { // Make sure we always have capacity > 0: https://github.com/napi-rs/napi-rs/issues/1124. let mut dest = String::with_capacity(1); let project_root = options.project_root.clone(); - let mut printer = Printer::new(&mut dest, options); - - #[cfg(feature = "sourcemap")] - { - printer.sources = Some(&self.sources); - } + let printer = Printer::new(&mut dest, options); + #[cfg(feature = "custom_sourcemap")] + let mut printer = printer.with_source_map(source_map); + #[cfg(not(feature = "custom_sourcemap"))] + let mut printer = printer; + printer.sources = Some(&self.sources); - #[cfg(feature = "sourcemap")] + #[cfg(feature = "custom_sourcemap")] if printer.source_map.is_some() { - printer.source_maps = self.sources.iter().enumerate().map(|(i, _)| self.source_map(i)).collect(); + printer.source_maps = self.sources.iter().enumerate().map(|(i, _)| self.source_map::(i)).collect(); } + let (dependencies, exports, references) = self.print(&mut printer, project_root)?; + Ok(ToCssResult { + dependencies, + exports, + code: dest, + references, + }) + } + + fn print<'a, 'c, P: PrinterTrait>( + &'c self, + printer: &mut P, + project_root: Option<&'a str>, + ) -> Result< + ( + Option>, + Option, + Option, + ), + Error, + > + where + 'a: 'c, + { for comment in &self.license_comments { printer.write_str("/*")?; printer.write_str_with_newlines(comment)?; @@ -290,35 +331,30 @@ where if let Some(config) = &self.options.css_modules { let mut references = CssModuleReferences::new(); - printer.css_module = Some(CssModule::new( + let css_module = CssModule::new( config, &self.sources, project_root, &mut references, &self.content_hashes, - )); + ); + printer.state_mut().css_module = Some(unsafe { std::mem::transmute(css_module) }); - self.rules.to_css(&mut printer)?; + self.rules.to_css(printer)?; printer.newline()?; - Ok(ToCssResult { - dependencies: printer.dependencies, - exports: Some(std::mem::take( - &mut printer.css_module.unwrap().exports_by_source_index[0], - )), - code: dest, - references: Some(references), - }) + let dependencies = printer.state_mut().dependencies.take(); + let mut css_module = printer.state_mut().css_module.take().unwrap(); + let exports = Some(std::mem::take(&mut css_module.exports_by_source_index[0])); + + Ok((dependencies, exports, Some(references))) } else { - self.rules.to_css(&mut printer)?; + self.rules.to_css(printer)?; printer.newline()?; - Ok(ToCssResult { - dependencies: printer.dependencies, - code: dest, - exports: None, - references: None, - }) + let dependencies = printer.state_mut().dependencies.take(); + + Ok((dependencies, None, None)) } } } @@ -397,12 +433,6 @@ impl<'i> StyleAttribute<'i> { /// Serializes the style attribute to a CSS string. pub fn to_css(&self, options: PrinterOptions) -> Result { - #[cfg(feature = "sourcemap")] - assert!( - options.source_map.is_none(), - "Source maps are not supported for style attributes" - ); - // Make sure we always have capacity > 0: https://github.com/napi-rs/napi-rs/issues/1124. let mut dest = String::with_capacity(1); let mut printer = Printer::new(&mut dest, options); @@ -411,7 +441,7 @@ impl<'i> StyleAttribute<'i> { self.declarations.to_css(&mut printer)?; Ok(ToCssResult { - dependencies: printer.dependencies, + dependencies: printer.take_dependencies(), code: dest, exports: None, references: None, diff --git a/src/traits.rs b/src/traits.rs index 07bc396b9..da5781b3e 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -3,7 +3,7 @@ use crate::context::PropertyHandlerContext; use crate::declaration::{DeclarationBlock, DeclarationList}; use crate::error::{ParserError, PrinterError}; -use crate::printer::Printer; +use crate::printer::{PrinterTrait, Printer}; use crate::properties::{Property, PropertyId}; use crate::stylesheet::{ParserOptions, PrinterOptions}; use crate::targets::{Browsers, Targets}; @@ -87,9 +87,7 @@ impl<'i, T: Parse<'i>> ParseWithOptions<'i> for T { /// Trait for things the can serialize themselves in CSS syntax. pub trait ToCss { /// Serialize `self` in CSS syntax, writing to `dest`. - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write; + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError>; /// Serialize `self` in CSS syntax and return a string. /// @@ -109,28 +107,19 @@ impl<'a, T> ToCss for &'a T where T: ToCss + ?Sized, { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { (*self).to_css(dest) } } impl ToCss for Box { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { (**self).to_css(dest) } } impl ToCss for Option { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { if let Some(v) = self { v.to_css(dest)?; } diff --git a/src/values/alpha.rs b/src/values/alpha.rs index 364933994..598bd6494 100644 --- a/src/values/alpha.rs +++ b/src/values/alpha.rs @@ -2,7 +2,6 @@ use super::percentage::NumberOrPercentage; use crate::error::{ParserError, PrinterError}; -use crate::printer::Printer; use crate::traits::{Parse, ToCss}; #[cfg(feature = "visitor")] use crate::visitor::Visit; @@ -29,10 +28,7 @@ impl<'i> Parse<'i> for AlphaValue { } impl ToCss for AlphaValue { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { self.0.to_css(dest) } } diff --git a/src/values/angle.rs b/src/values/angle.rs index b6e004192..6d1bef750 100644 --- a/src/values/angle.rs +++ b/src/values/angle.rs @@ -5,7 +5,6 @@ use super::length::serialize_dimension; use super::number::{CSSNumber, NumericRange}; use super::percentage::DimensionPercentage; use crate::error::{ParserError, PrinterError}; -use crate::printer::Printer; use crate::traits::{ impl_op, private::{AddInternal, TryAdd}, @@ -113,10 +112,7 @@ impl<'i> TryFrom<&Token<'i>> for Angle { } impl ToCss for Angle { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { let (value, unit) = match self { Angle::Deg(val) => (*val, "deg"), Angle::Grad(val) => (*val, "grad"), @@ -141,10 +137,10 @@ impl ToCss for Angle { impl Angle { /// Prints the angle, allowing unitless zero values. - pub fn to_css_with_unitless_zero(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + pub fn to_css_with_unitless_zero( + &self, + dest: &mut PrinterT, + ) -> Result<(), PrinterError> { if self.is_zero() { (0.0).to_css(dest) } else { diff --git a/src/values/calc.rs b/src/values/calc.rs index e24eb2945..5b675bb04 100644 --- a/src/values/calc.rs +++ b/src/values/calc.rs @@ -3,7 +3,6 @@ use crate::compat::Feature; use crate::error::{ParserError, PrinterError}; use crate::macros::enum_property; -use crate::printer::Printer; use crate::targets::{should_compile, Browsers}; use crate::traits::private::AddInternal; use crate::traits::{IsCompatible, Parse, Sign, ToCss, TryMap, TryOp, TrySign}; @@ -123,15 +122,13 @@ fn modulo(a: f32, b: f32) -> f32 { } impl + TrySign + Clone + std::fmt::Debug> ToCss for MathFunction { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { MathFunction::Calc(calc) => { dest.write_str("calc(")?; calc.to_css(dest)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } MathFunction::Min(args) => { dest.write_str("min(")?; @@ -144,7 +141,8 @@ impl + TrySign + Clone + std::fmt::Deb } arg.to_css(dest)?; } - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } MathFunction::Max(args) => { dest.write_str("max(")?; @@ -157,11 +155,12 @@ impl + TrySign + Clone + std::fmt::Deb } arg.to_css(dest)?; } - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } MathFunction::Clamp(a, b, c) => { // If clamp() is unsupported by targets, output min()/max() - if should_compile!(dest.targets.current, ClampFunction) { + if should_compile!(dest.state().targets.current, ClampFunction) { dest.write_str("max(")?; a.to_css(dest)?; dest.delim(',', false)?; @@ -179,7 +178,8 @@ impl + TrySign + Clone + std::fmt::Deb b.to_css(dest)?; dest.delim(',', false)?; c.to_css(dest)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } MathFunction::Round(strategy, a, b) => { dest.write_str("round(")?; @@ -190,31 +190,36 @@ impl + TrySign + Clone + std::fmt::Deb a.to_css(dest)?; dest.delim(',', false)?; b.to_css(dest)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } MathFunction::Rem(a, b) => { dest.write_str("rem(")?; a.to_css(dest)?; dest.delim(',', false)?; b.to_css(dest)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } MathFunction::Mod(a, b) => { dest.write_str("mod(")?; a.to_css(dest)?; dest.delim(',', false)?; b.to_css(dest)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } MathFunction::Abs(v) => { dest.write_str("abs(")?; v.to_css(dest)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } MathFunction::Sign(v) => { dest.write_str("sign(")?; v.to_css(dest)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } MathFunction::Hypot(args) => { dest.write_str("hypot(")?; @@ -227,7 +232,8 @@ impl + TrySign + Clone + std::fmt::Deb } arg.to_css(dest)?; } - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } } } @@ -958,13 +964,9 @@ impl> + std::convert::TryFrom + TrySign + Clone + std::fmt::Debug> ToCss for Calc { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - let was_in_calc = dest.in_calc; - dest.in_calc = true; - + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { + let old_in_calc = dest.state().in_calc; + dest.state_mut().in_calc = true; let res = match self { Calc::Value(v) => v.to_css(dest), Calc::Number(n) => n.to_css(dest), @@ -995,8 +997,7 @@ impl + TrySign + Clone + std::fmt::Deb } Calc::Function(f) => f.to_css(dest), }; - - dest.in_calc = was_in_calc; + dest.state_mut().in_calc = old_in_calc; res } } diff --git a/src/values/color.rs b/src/values/color.rs index bb4ec1487..7cecc44a5 100644 --- a/src/values/color.rs +++ b/src/values/color.rs @@ -9,7 +9,6 @@ use super::percentage::Percentage; use crate::compat::Feature; use crate::error::{ParserError, PrinterError}; use crate::macros::enum_property; -use crate::printer::Printer; use crate::properties::PropertyId; use crate::rules::supports::SupportsCondition; use crate::targets::{should_compile, Browsers, Features, Targets}; @@ -22,7 +21,6 @@ use cssparser::*; use cssparser_color::{hsl_to_rgb, AngleOrNumber, ColorParser, NumberOrPercentage}; use std::any::TypeId; use std::f32::consts::PI; -use std::fmt::Write; /// A CSS [``](https://www.w3.org/TR/css-color-4/#color-type) value. /// @@ -631,17 +629,17 @@ impl<'i> Parse<'i> for CssColor { } impl ToCss for CssColor { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { - CssColor::CurrentColor => dest.write_str("currentColor"), + CssColor::CurrentColor => { + dest.write_str("currentColor")?; + Ok(()) + } CssColor::RGBA(color) => { if color.alpha == 255 { let hex: u32 = ((color.red as u32) << 16) | ((color.green as u32) << 8) | (color.blue as u32); if let Some(name) = short_color_name(hex) { - return dest.write_str(name); + return Ok(dest.write_str(name)?); } let compact = compact_hex(hex); @@ -652,11 +650,11 @@ impl ToCss for CssColor { } } else { // If the #rrggbbaa syntax is not supported by the browser targets, output rgba() - if should_compile!(dest.targets.current, HexAlphaColors) { + if should_compile!(dest.state().targets.current, HexAlphaColors) { // If the browser doesn't support `#rrggbbaa` color syntax, it is converted to `transparent` when compressed(minify = true). // https://www.w3.org/TR/css-color-4/#transparent-black - if dest.minify && color.red == 0 && color.green == 0 && color.blue == 0 && color.alpha == 0 { - return dest.write_str("transparent"); + if dest.options().minify && color.red == 0 && color.green == 0 && color.blue == 0 && color.alpha == 0 { + return Ok(dest.write_str("transparent")?); } else { dest.write_str("rgba(")?; write!(dest, "{}", color.red)?; @@ -705,7 +703,7 @@ impl ToCss for CssColor { CssColor::from(rgb).to_css(dest) } CssColor::LightDark(light, dark) => { - if should_compile!(dest.targets.current, LightDark) { + if should_compile!(dest.state().targets.current, LightDark) { dest.write_str("var(--lightningcss-light")?; dest.delim(',', false)?; light.to_css(dest)?; @@ -714,14 +712,15 @@ impl ToCss for CssColor { dest.write_str("var(--lightningcss-dark")?; dest.delim(',', false)?; dark.to_css(dest)?; - return dest.write_char(')'); + return Ok(dest.write_char(')')?); } dest.write_str("light-dark(")?; light.to_css(dest)?; dest.delim(',', false)?; dark.to_css(dest)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } CssColor::System(system) => system.to_css(dest), } @@ -1540,17 +1539,14 @@ fn parse_legacy_alpha<'i, 't>( } #[inline] -fn write_components( +fn write_components( name: &str, a: f32, b: f32, c: f32, alpha: f32, - dest: &mut Printer, -) -> Result<(), PrinterError> -where - W: std::fmt::Write, -{ + dest: &mut PrinterT, +) -> Result<(), PrinterError> { dest.write_str(name)?; dest.write_char('(')?; if a.is_nan() { @@ -1567,14 +1563,13 @@ where write_component(alpha, dest)?; } - dest.write_char(')') + dest.write_char(')')?; + + Ok(()) } #[inline] -fn write_component(c: f32, dest: &mut Printer) -> Result<(), PrinterError> -where - W: std::fmt::Write, -{ +fn write_component(c: f32, dest: &mut PrinterT) -> Result<(), PrinterError> { if c.is_nan() { dest.write_str("none")?; } else { @@ -1584,10 +1579,10 @@ where } #[inline] -fn write_predefined(predefined: &PredefinedColor, dest: &mut Printer) -> Result<(), PrinterError> -where - W: std::fmt::Write, -{ +fn write_predefined( + predefined: &PredefinedColor, + dest: &mut PrinterT, +) -> Result<(), PrinterError> { use PredefinedColor::*; let (name, a, b, c, alpha) = match predefined { @@ -1616,7 +1611,9 @@ where write_component(alpha, dest)?; } - dest.write_char(')') + dest.write_char(')')?; + + Ok(()) } bitflags! { diff --git a/src/values/easing.rs b/src/values/easing.rs index 4db5c063d..019987828 100644 --- a/src/values/easing.rs +++ b/src/values/easing.rs @@ -1,13 +1,11 @@ //! CSS easing functions. use crate::error::{ParserError, PrinterError}; -use crate::printer::Printer; use crate::traits::{Parse, ToCss}; use crate::values::number::{CSSInteger, CSSNumber}; #[cfg(feature = "visitor")] use crate::visitor::Visit; use cssparser::*; -use std::fmt::Write; /// A CSS [easing function](https://www.w3.org/TR/css-easing-1/#easing-functions). #[derive(Debug, Clone, PartialEq)] @@ -110,17 +108,32 @@ impl<'i> Parse<'i> for EasingFunction { } impl ToCss for EasingFunction { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { - EasingFunction::Linear => dest.write_str("linear"), - EasingFunction::Ease => dest.write_str("ease"), - EasingFunction::EaseIn => dest.write_str("ease-in"), - EasingFunction::EaseOut => dest.write_str("ease-out"), - EasingFunction::EaseInOut => dest.write_str("ease-in-out"), - _ if self.is_ease() => dest.write_str("ease"), + EasingFunction::Linear => { + dest.write_str("linear")?; + Ok(()) + } + EasingFunction::Ease => { + dest.write_str("ease")?; + Ok(()) + } + EasingFunction::EaseIn => { + dest.write_str("ease-in")?; + Ok(()) + } + EasingFunction::EaseOut => { + dest.write_str("ease-out")?; + Ok(()) + } + EasingFunction::EaseInOut => { + dest.write_str("ease-in-out")?; + Ok(()) + } + _ if self.is_ease() => { + dest.write_str("ease")?; + Ok(()) + } x if *x == EasingFunction::CubicBezier { x1: 0.42, @@ -129,7 +142,8 @@ impl ToCss for EasingFunction { y2: 1.0, } => { - dest.write_str("ease-in") + dest.write_str("ease-in")?; + Ok(()) } x if *x == EasingFunction::CubicBezier { @@ -139,7 +153,8 @@ impl ToCss for EasingFunction { y2: 1.0, } => { - dest.write_str("ease-out") + dest.write_str("ease-out")?; + Ok(()) } x if *x == EasingFunction::CubicBezier { @@ -149,7 +164,8 @@ impl ToCss for EasingFunction { y2: 1.0, } => { - dest.write_str("ease-in-out") + dest.write_str("ease-in-out")?; + Ok(()) } EasingFunction::CubicBezier { x1, y1, x2, y2 } => { dest.write_str("cubic-bezier(")?; @@ -160,22 +176,30 @@ impl ToCss for EasingFunction { x2.to_css(dest)?; dest.delim(',', false)?; y2.to_css(dest)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } EasingFunction::Steps { count: 1, position: StepPosition::Start, - } => dest.write_str("step-start"), + } => { + dest.write_str("step-start")?; + Ok(()) + } EasingFunction::Steps { count: 1, position: StepPosition::End, - } => dest.write_str("step-end"), + } => { + dest.write_str("step-end")?; + Ok(()) + } EasingFunction::Steps { count, position } => { dest.write_str("steps(")?; write!(dest, "{}", count)?; dest.delim(',', false)?; position.to_css(dest)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } } } diff --git a/src/values/gradient.rs b/src/values/gradient.rs index e3369ba3e..c440a8b98 100644 --- a/src/values/gradient.rs +++ b/src/values/gradient.rs @@ -11,7 +11,6 @@ use crate::compat; use crate::error::{ParserError, PrinterError}; use crate::macros::enum_property; use crate::prefixes::Feature; -use crate::printer::Printer; use crate::targets::{should_compile, Browsers, Targets}; use crate::traits::{IsCompatible, Parse, ToCss, TrySign, Zero}; use crate::values::number::NonNegative; @@ -192,10 +191,7 @@ impl<'i> Parse<'i> for Gradient { } impl ToCss for Gradient { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { let (f, prefix) = match self { Gradient::Linear(g) => ("linear-gradient(", Some(g.vendor_prefix)), Gradient::RepeatingLinear(g) => ("repeating-linear-gradient(", Some(g.vendor_prefix)), @@ -221,7 +217,9 @@ impl ToCss for Gradient { Gradient::WebKitGradient(g) => g.to_css(dest)?, } - dest.write_char(')') + dest.write_char(')')?; + + Ok(()) } } @@ -265,10 +263,11 @@ impl LinearGradient { }) } - fn to_css(&self, dest: &mut Printer, is_prefixed: bool) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css( + &self, + dest: &mut PrinterT, + is_prefixed: bool, + ) -> Result<(), PrinterError> { let angle = match &self.direction { LineDirection::Vertical(VerticalPositionKeyword::Bottom) => 180.0, LineDirection::Vertical(VerticalPositionKeyword::Top) => 0.0, @@ -283,7 +282,7 @@ impl LinearGradient { // If we have `to top` or `0deg`, and all of the positions and hints are percentages, // we can flip the gradient the other direction and omit the direction. } else if angle == 0.0 - && dest.minify + && dest.options().minify && self.items.iter().all(|item| { matches!( item, @@ -393,10 +392,7 @@ impl<'i> RadialGradient { } impl ToCss for RadialGradient { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { if self.shape != EndingShape::default() { self.shape.to_css(dest)?; if self.position.is_center() { @@ -498,17 +494,24 @@ impl LineDirection { Ok(LineDirection::Vertical(y)) } - fn to_css(&self, dest: &mut Printer, is_prefixed: bool) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css( + &self, + dest: &mut PrinterT, + is_prefixed: bool, + ) -> Result<(), PrinterError> { match self { LineDirection::Angle(angle) => angle.to_css(dest), LineDirection::Horizontal(k) => { - if dest.minify { + if dest.options().minify { match k { - HorizontalPositionKeyword::Left => dest.write_str("270deg"), - HorizontalPositionKeyword::Right => dest.write_str("90deg"), + HorizontalPositionKeyword::Left => { + dest.write_str("270deg")?; + Ok(()) + } + HorizontalPositionKeyword::Right => { + dest.write_str("90deg")?; + Ok(()) + } } } else { if !is_prefixed { @@ -518,10 +521,16 @@ impl LineDirection { } } LineDirection::Vertical(k) => { - if dest.minify { + if dest.options().minify { match k { - VerticalPositionKeyword::Top => dest.write_str("0deg"), - VerticalPositionKeyword::Bottom => dest.write_str("180deg"), + VerticalPositionKeyword::Top => { + dest.write_str("0deg")?; + Ok(()) + } + VerticalPositionKeyword::Bottom => { + dest.write_str("180deg")?; + Ok(()) + } } } else { if !is_prefixed { @@ -677,10 +686,7 @@ impl<'i> Parse<'i> for Circle { } impl ToCss for Circle { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { Circle::Radius(r) => r.to_css(dest), Circle::Extent(extent) => { @@ -757,10 +763,7 @@ impl<'i> Parse<'i> for Ellipse { } impl ToCss for Ellipse { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { // The `ellipse` keyword is optional, so we don't emit it. match self { Ellipse::Size { x, y } => { @@ -832,10 +835,7 @@ impl ConicGradient { } impl ToCss for ConicGradient { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { if !self.angle.is_zero() { dest.write_str("from ")?; self.angle.to_css(dest)?; @@ -897,10 +897,7 @@ impl<'i, D: Parse<'i>> Parse<'i> for ColorStop { } impl ToCss for ColorStop { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { self.color.to_css(dest)?; if let Some(position) = &self.position { dest.write_char(' ')?; @@ -937,10 +934,7 @@ pub enum GradientItem { } impl ToCss for GradientItem { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { GradientItem::ColorStop(stop) => stop.to_css(dest), GradientItem::Hint(hint) => hint.to_css(dest), @@ -1024,14 +1018,11 @@ fn parse_items<'i, 't, D: Parse<'i>>( fn serialize_items< D: ToCss + std::cmp::PartialEq + std::ops::Mul + TrySign + Clone + std::fmt::Debug, - W, + PrinterT: crate::printer::PrinterTrait, >( items: &Vec>>, - dest: &mut Printer, -) -> Result<(), PrinterError> -where - W: std::fmt::Write, -{ + dest: &mut PrinterT, +) -> Result<(), PrinterError> { let mut first = true; let mut last: Option<&GradientItem>> = None; for item in items { @@ -1042,7 +1033,7 @@ where // Use double position stop if the last stop is the same color and all targets support it. if let Some(prev) = last { - if !should_compile!(dest.targets.current, DoublePositionGradients) { + if !should_compile!(dest.state().targets.current, DoublePositionGradients) { match (prev, item) { ( GradientItem::ColorStop(ColorStop { @@ -1153,10 +1144,7 @@ impl<'i> Parse<'i> for WebKitGradient { } impl ToCss for WebKitGradient { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { WebKitGradient::Linear { from, to, stops } => { dest.write_str("linear")?; @@ -1256,10 +1244,7 @@ impl<'i> Parse<'i> for WebKitColorStop { } impl ToCss for WebKitColorStop { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { if self.position == 0.0 { dest.write_str("from(")?; self.color.to_css(dest)?; @@ -1272,7 +1257,8 @@ impl ToCss for WebKitColorStop { dest.delim(',', false)?; self.color.to_css(dest)?; } - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } } @@ -1307,10 +1293,7 @@ impl<'i> Parse<'i> for WebKitGradientPoint { } impl ToCss for WebKitGradientPoint { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { self.x.to_css(dest)?; dest.write_char(' ')?; self.y.to_css(dest) @@ -1352,28 +1335,28 @@ impl<'i, S: Parse<'i>> Parse<'i> for WebKitGradientPointComponent { } impl> ToCss for WebKitGradientPointComponent { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { use WebKitGradientPointComponent::*; match &self { Center => { - if dest.minify { - dest.write_str("50%") + if dest.options().minify { + dest.write_str("50%")?; + Ok(()) } else { - dest.write_str("center") + dest.write_str("center")?; + Ok(()) } } Number(lp) => { if matches!(lp, NumberOrPercentage::Percentage(Percentage(p)) if *p == 0.0) { - dest.write_char('0') + dest.write_char('0')?; + Ok(()) } else { lp.to_css(dest) } } Side(s) => { - if dest.minify { + if dest.options().minify { let lp: LengthPercentage = s.clone().into(); lp.to_css(dest)?; } else { diff --git a/src/values/ident.rs b/src/values/ident.rs index be850f8b6..3675491e0 100644 --- a/src/values/ident.rs +++ b/src/values/ident.rs @@ -1,7 +1,6 @@ //! CSS identifiers. use crate::error::{ParserError, PrinterError}; -use crate::printer::Printer; use crate::properties::css_modules::Specifier; use crate::traits::{Parse, ParseWithOptions, ToCss}; use crate::values::string::CowArcStr; @@ -45,26 +44,21 @@ impl<'i> Parse<'i> for CustomIdent<'i> { } impl<'i> ToCss for CustomIdent<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { self.to_css_with_options(dest, true) } } impl<'i> CustomIdent<'i> { /// Write the custom ident to CSS. - pub(crate) fn to_css_with_options( + pub(crate) fn to_css_with_options( &self, - dest: &mut Printer, + dest: &mut PrinterT, enabled_css_modules: bool, - ) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + ) -> Result<(), PrinterError> { let css_module_custom_idents_enabled = enabled_css_modules && dest + .state_mut() .css_module .as_mut() .map_or(false, |css_module| css_module.config.custom_idents); @@ -116,12 +110,12 @@ impl<'i> Parse<'i> for NoneOrCustomIdentList<'i> { } impl<'i> ToCss for NoneOrCustomIdentList<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { - NoneOrCustomIdentList::None => dest.write_str("none"), + NoneOrCustomIdentList::None => { + dest.write_str("none")?; + Ok(()) + } NoneOrCustomIdentList::Idents(types) => { let mut first = true; for ident in types { @@ -163,10 +157,7 @@ impl<'i> Parse<'i> for DashedIdent<'i> { } impl<'i> ToCss for DashedIdent<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { dest.write_dashed_ident(&self.0, true) } } @@ -230,13 +221,11 @@ impl<'i> ParseWithOptions<'i> for DashedIdentReference<'i> { } impl<'i> ToCss for DashedIdentReference<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - match &mut dest.css_module { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { + let source_index = dest.state().loc.source_index; + match dest.state_mut().css_module.as_mut() { Some(css_module) if css_module.config.dashed_idents => { - if let Some(name) = css_module.reference_dashed(&self.ident.0, &self.from, dest.loc.source_index) { + if let Some(name) = css_module.reference_dashed(&self.ident.0, &self.from, source_index) { dest.write_str("--")?; serialize_name(&name, dest)?; return Ok(()); @@ -265,10 +254,7 @@ impl<'i> Parse<'i> for Ident<'i> { } impl<'i> ToCss for Ident<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { serialize_identifier(&self.0, dest)?; Ok(()) } diff --git a/src/values/image.rs b/src/values/image.rs index 4964c98c9..a8d400df4 100644 --- a/src/values/image.rs +++ b/src/values/image.rs @@ -7,7 +7,6 @@ use crate::compat; use crate::dependencies::{Dependency, UrlDependency}; use crate::error::{ParserError, PrinterError}; use crate::prefixes::{is_webkit_gradient, Feature}; -use crate::printer::Printer; use crate::targets::{Browsers, Targets}; use crate::traits::{FallbackValues, IsCompatible, Parse, ToCss}; use crate::values::string::CowArcStr; @@ -368,10 +367,7 @@ impl<'i> Parse<'i> for ImageSet<'i> { } impl<'i> ToCss for ImageSet<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { self.vendor_prefix.to_css(dest)?; dest.write_str("image-set(")?; let mut first = true; @@ -383,7 +379,8 @@ impl<'i> ToCss for ImageSet<'i> { } option.to_css(dest, self.vendor_prefix != VendorPrefix::None)?; } - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } } @@ -445,22 +442,23 @@ impl<'i> Parse<'i> for ImageSetOption<'i> { } impl<'i> ImageSetOption<'i> { - fn to_css(&self, dest: &mut Printer, is_prefixed: bool) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css( + &self, + dest: &mut PrinterT, + is_prefixed: bool, + ) -> Result<(), PrinterError> { match &self.image { // Prefixed syntax didn't allow strings, only url() Image::Url(url) if !is_prefixed => { // Add dependency if needed. Normally this is handled by the Url type. - let dep = if dest.dependencies.is_some() { + let dep = if dest.state().dependencies.is_some() { Some(UrlDependency::new(url, dest.filename())) } else { None }; if let Some(dep) = dep { serialize_string(&dep.placeholder, dest)?; - if let Some(dependencies) = &mut dest.dependencies { + if let Some(dependencies) = dest.state_mut().dependencies.as_mut() { dependencies.push(Dependency::Url(dep)) } } else { @@ -478,9 +476,11 @@ impl<'i> ImageSetOption<'i> { // Safari only supports the x resolution unit in image-set(). // In other places, x was added as an alias later. // Temporarily ignore the targets while printing here. - let targets = std::mem::take(&mut dest.targets.current); - self.resolution.to_css(dest)?; - dest.targets.current = targets; + let targets = dest.state().targets.current; + dest.state_mut().targets.current = Targets::default(); + let res = self.resolution.to_css(dest); + dest.state_mut().targets.current = targets; + res?; if let Some(file_type) = &self.file_type { dest.write_str(" type(")?; diff --git a/src/values/length.rs b/src/values/length.rs index e0b4be178..e0e1d8d0d 100644 --- a/src/values/length.rs +++ b/src/values/length.rs @@ -5,7 +5,6 @@ use super::calc::{Calc, MathFunction}; use super::number::{CSSNumber, NumericRange}; use super::percentage::DimensionPercentage; use crate::error::{ParserError, PrinterError}; -use crate::printer::Printer; use crate::targets::Browsers; use crate::traits::{ private::{AddInternal, TryAdd}, @@ -27,10 +26,10 @@ impl LengthPercentage { LengthPercentage::Dimension(LengthValue::Px(val)) } - pub(crate) fn to_css_unitless(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + pub(crate) fn to_css_unitless( + &self, + dest: &mut PrinterT, + ) -> Result<(), PrinterError> { match self { DimensionPercentage::Dimension(d) => d.to_css_unitless(dest), _ => self.to_css(dest), @@ -482,16 +481,13 @@ define_length_units! { } impl ToCss for LengthValue { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { let (value, unit) = self.to_unit_value(); // The unit can be omitted if the value is zero, except inside calc() // expressions, where unitless numbers won't be parsed as dimensions. - if !dest.in_calc && value == 0.0 { - return dest.write_char('0'); + if !dest.state().in_calc && value == 0.0 { + return Ok(dest.write_char('0')?); } serialize_dimension(value, unit, dest) @@ -499,10 +495,10 @@ impl ToCss for LengthValue { } impl LengthValue { - pub(crate) fn to_css_unitless(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + pub(crate) fn to_css_unitless( + &self, + dest: &mut PrinterT, + ) -> Result<(), PrinterError> { match self { LengthValue::Px(value) => value.to_css(dest), _ => self.to_css(dest), @@ -510,10 +506,11 @@ impl LengthValue { } } -pub(crate) fn serialize_dimension(value: f32, unit: &str, dest: &mut Printer) -> Result<(), PrinterError> -where - W: std::fmt::Write, -{ +pub(crate) fn serialize_dimension( + value: f32, + unit: &str, + dest: &mut PrinterT, +) -> Result<(), PrinterError> { use cssparser::ToCss; let int_value = if value.fract() == 0.0 { Some(value as i32) } else { None }; let token = Token::Dimension { @@ -528,12 +525,15 @@ where if value < 0.0 { if let Some(stripped) = s.strip_prefix("-0") { dest.write_char('-')?; - dest.write_str(stripped) + dest.write_str(stripped)?; + Ok(()) } else { - dest.write_str(&s) + dest.write_str(&s)?; + Ok(()) } } else { - dest.write_str(s.trim_start_matches('0')) + dest.write_str(s.trim_start_matches('0'))?; + Ok(()) } } else { token.to_css(dest)?; @@ -611,10 +611,7 @@ impl<'i> ParseNumeric<'i> for Length { } impl ToCss for Length { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { Length::Value(a) => a.to_css(dest), Length::Calc(c) => c.to_css(dest), diff --git a/src/values/number.rs b/src/values/number.rs index b5cfe010b..f02d54eaf 100644 --- a/src/values/number.rs +++ b/src/values/number.rs @@ -3,7 +3,6 @@ use super::angle::impl_try_from_angle; use super::calc::Calc; use crate::error::{ParserError, PrinterError}; -use crate::printer::Printer; use crate::traits::private::AddInternal; use crate::traits::{IsCompatible, Map, Op, Parse, ParseNumeric, Sign, ToCss, TrySign, Zero}; #[cfg(feature = "visitor")] @@ -120,19 +119,18 @@ impl<'i> ParseNumeric<'i> for CSSNumber { } impl ToCss for CSSNumber { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { let number = *self; if number != 0.0 && number.abs() < 1.0 { let mut s = String::new(); cssparser::ToCss::to_css(self, &mut s)?; if number < 0.0 { dest.write_char('-')?; - dest.write_str(s.trim_start_matches("-").trim_start_matches("0")) + dest.write_str(s.trim_start_matches("-").trim_start_matches("0"))?; + Ok(()) } else { - dest.write_str(s.trim_start_matches('0')) + dest.write_str(s.trim_start_matches('0'))?; + Ok(()) } } else { cssparser::ToCss::to_css(self, dest)?; @@ -223,10 +221,7 @@ impl<'i> ParseNumeric<'i> for CSSInteger { } impl ToCss for CSSInteger { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { cssparser::ToCss::to_css(self, dest)?; Ok(()) } diff --git a/src/values/percentage.rs b/src/values/percentage.rs index 842da3d82..5405a76c2 100644 --- a/src/values/percentage.rs +++ b/src/values/percentage.rs @@ -4,7 +4,6 @@ use super::angle::{impl_try_from_angle, Angle}; use super::calc::{Calc, MathFunction}; use super::number::{CSSNumber, NumericRange}; use crate::error::{ParserError, PrinterError}; -use crate::printer::Printer; use crate::traits::private::AddInternal; use crate::traits::{ impl_op, private::TryAdd, Op, Parse, ParseNumeric, Sign, ToCss, TryMap, TryOp, TrySign, Zero, @@ -49,10 +48,7 @@ impl<'i> ParseNumeric<'i> for Percentage { } impl ToCss for Percentage { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { use cssparser::ToCss; let v = self.0 * 100.0; let int_value = if (v).fract() == 0.0 { Some(v as i32) } else { None }; @@ -66,9 +62,11 @@ impl ToCss for Percentage { percent.to_css(&mut s)?; if self.0 < 0.0 { dest.write_char('-')?; - dest.write_str(s.trim_start_matches("-0")) + dest.write_str(s.trim_start_matches("-0"))?; + Ok(()) } else { - dest.write_str(s.trim_start_matches('0')) + dest.write_str(s.trim_start_matches('0'))?; + Ok(()) } } else { percent.to_css(dest)?; @@ -542,10 +540,7 @@ impl TrySign for DimensionPercentage { impl + TrySign + Clone + std::fmt::Debug> ToCss for DimensionPercentage { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { DimensionPercentage::Dimension(length) => length.to_css(dest), DimensionPercentage::Percentage(percent) => percent.to_css(dest), diff --git a/src/values/position.rs b/src/values/position.rs index 32e4788d4..688cfaaf0 100644 --- a/src/values/position.rs +++ b/src/values/position.rs @@ -4,7 +4,6 @@ use super::length::LengthPercentage; use super::percentage::Percentage; use crate::error::{ParserError, PrinterError}; use crate::macros::enum_property; -use crate::printer::Printer; use crate::targets::Browsers; use crate::traits::{IsCompatible, Parse, ToCss, Zero}; #[cfg(feature = "visitor")] @@ -168,10 +167,7 @@ impl<'i> Parse<'i> for Position { } impl ToCss for Position { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match (&self.x, &self.y) { (x_pos @ &HorizontalPosition::Side { side, offset: Some(_) }, &VerticalPosition::Length(ref y_lp)) if side != HorizontalPositionKeyword::Left => @@ -185,7 +181,8 @@ impl ToCss for Position { { // If there is a side keyword with an offset, "center" must be a keyword not a percentage. x_pos.to_css(dest)?; - dest.write_str(" center") + dest.write_str(" center")?; + Ok(()) } (&HorizontalPosition::Length(ref x_lp), y_pos @ &VerticalPosition::Side { side, offset: Some(_) }) if side != VerticalPositionKeyword::Top => @@ -360,17 +357,16 @@ impl<'i, S: Parse<'i>> Parse<'i> for PositionComponent { } impl ToCss for PositionComponent { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { use PositionComponent::*; match &self { Center => { - if dest.minify { - dest.write_str("50%") + if dest.options().minify { + dest.write_str("50%")?; + Ok(()) } else { - dest.write_str("center") + dest.write_str("center")?; + Ok(()) } } Length(lp) => lp.to_css(dest), diff --git a/src/values/ratio.rs b/src/values/ratio.rs index c724f6250..f6522fec5 100644 --- a/src/values/ratio.rs +++ b/src/values/ratio.rs @@ -2,7 +2,6 @@ use super::number::{CSSNumber, NonNegative}; use crate::error::{ParserError, PrinterError}; -use crate::printer::Printer; use crate::traits::{Parse, ToCss}; #[cfg(feature = "visitor")] use crate::visitor::Visit; @@ -42,10 +41,7 @@ impl Ratio { } impl ToCss for Ratio { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { self.0.to_css(dest)?; if self.1 .0 != 1.0 { dest.delim('/', true)?; diff --git a/src/values/rect.rs b/src/values/rect.rs index 49b4e6ce7..14be6827f 100644 --- a/src/values/rect.rs +++ b/src/values/rect.rs @@ -1,7 +1,6 @@ //! Generic values for four sided properties. use crate::error::{ParserError, PrinterError}; -use crate::printer::Printer; use crate::traits::{IsCompatible, Parse, ToCss}; #[cfg(feature = "visitor")] use crate::visitor::Visit; @@ -95,10 +94,7 @@ impl ToCss for Rect where T: PartialEq + ToCss, { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { self.0.to_css(dest)?; let same_vertical = self.0 == self.2; let same_horizontal = self.1 == self.3; diff --git a/src/values/resolution.rs b/src/values/resolution.rs index f8fdf3fb1..5969d75a4 100644 --- a/src/values/resolution.rs +++ b/src/values/resolution.rs @@ -4,7 +4,6 @@ use super::length::serialize_dimension; use super::number::{CSSNumber, NumericRange}; use crate::compat::Feature; use crate::error::{ParserError, PrinterError}; -use crate::printer::Printer; use crate::traits::{Parse, ParseNumeric, ToCss}; #[cfg(feature = "visitor")] use crate::visitor::Visit; @@ -75,15 +74,12 @@ impl<'i> TryFrom<&Token<'i>> for Resolution { } impl ToCss for Resolution { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { let (value, unit) = match self { Resolution::Dpi(dpi) => (*dpi, "dpi"), Resolution::Dpcm(dpcm) => (*dpcm, "dpcm"), Resolution::Dppx(dppx) => { - if dest.targets.current.is_compatible(Feature::XResolutionUnit) { + if dest.state().targets.current.is_compatible(Feature::XResolutionUnit) { (*dppx, "x") } else { (*dppx, "dppx") diff --git a/src/values/shape.rs b/src/values/shape.rs index 911644ba9..bb4f5c2b7 100644 --- a/src/values/shape.rs +++ b/src/values/shape.rs @@ -5,7 +5,6 @@ use super::position::Position; use super::rect::Rect; use crate::error::{ParserError, PrinterError}; use crate::macros::enum_property; -use crate::printer::Printer; use crate::properties::border_radius::BorderRadius; use crate::traits::{Parse, ToCss}; use crate::values::number::NonNegative; @@ -235,40 +234,38 @@ impl<'i> Parse<'i> for Point { } impl ToCss for BasicShape { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { BasicShape::Inset(rect) => { dest.write_str("inset(")?; rect.to_css(dest)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } BasicShape::Circle(circle) => { dest.write_str("circle(")?; circle.to_css(dest)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } BasicShape::Ellipse(ellipse) => { dest.write_str("ellipse(")?; ellipse.to_css(dest)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } BasicShape::Polygon(poly) => { dest.write_str("polygon(")?; poly.to_css(dest)?; - dest.write_char(')') + dest.write_char(')')?; + Ok(()) } } } } impl ToCss for InsetRect { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { self.rect.to_css(dest)?; if self.radius != BorderRadius::default() { dest.write_str(" round ")?; @@ -279,10 +276,7 @@ impl ToCss for InsetRect { } impl ToCss for Circle { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { let mut has_output = false; if self.radius != ShapeRadius::default() { self.radius.to_css(dest)?; @@ -302,10 +296,7 @@ impl ToCss for Circle { } impl ToCss for Ellipse { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { let mut has_output = false; if self.radius_x != ShapeRadius::default() || self.radius_y != ShapeRadius::default() { self.radius_x.to_css(dest)?; @@ -327,10 +318,7 @@ impl ToCss for Ellipse { } impl ToCss for Polygon { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { if self.fill_rule != FillRule::default() { self.fill_rule.to_css(dest)?; dest.delim(',', false)?; @@ -351,10 +339,7 @@ impl ToCss for Polygon { } impl ToCss for Point { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { self.x.to_css(dest)?; dest.write_char(' ')?; self.y.to_css(dest) diff --git a/src/values/size.rs b/src/values/size.rs index 79b96e308..350193260 100644 --- a/src/values/size.rs +++ b/src/values/size.rs @@ -1,7 +1,6 @@ //! Generic values for two component properties. use crate::error::{ParserError, PrinterError}; -use crate::printer::Printer; use crate::traits::{IsCompatible, Parse, ToCss}; #[cfg(feature = "visitor")] use crate::visitor::Visit; @@ -32,10 +31,7 @@ impl ToCss for Size2D where T: ToCss + PartialEq, { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { self.0.to_css(dest)?; if self.1 != self.0 { dest.write_str(" ")?; diff --git a/src/values/string.rs b/src/values/string.rs index 3829e29e9..bd1d24003 100644 --- a/src/values/string.rs +++ b/src/values/string.rs @@ -338,10 +338,10 @@ impl<'i> Parse<'i> for CSSString<'i> { } impl<'i> ToCss for CSSString<'i> { - fn to_css(&self, dest: &mut crate::printer::Printer) -> Result<(), crate::error::PrinterError> - where - W: std::fmt::Write, - { + fn to_css( + &self, + dest: &mut PrinterT, + ) -> Result<(), crate::error::PrinterError> { serialize_string(&self.0, dest)?; Ok(()) } diff --git a/src/values/syntax.rs b/src/values/syntax.rs index 306f5e413..ef49d276b 100644 --- a/src/values/syntax.rs +++ b/src/values/syntax.rs @@ -3,7 +3,6 @@ use super::ident::Ident; use super::number::{CSSInteger, CSSNumber}; use crate::error::{ParserError, PrinterError}; -use crate::printer::Printer; use crate::properties::custom::TokenList; use crate::stylesheet::ParserOptions; use crate::traits::{Parse, ToCss}; @@ -396,10 +395,7 @@ impl<'i> Parse<'i> for SyntaxString { } impl ToCss for SyntaxString { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { dest.write_char('"')?; match self { SyntaxString::Universal => dest.write_char('*')?, @@ -417,29 +413,31 @@ impl ToCss for SyntaxString { } } - dest.write_char('"') + dest.write_char('"')?; + + Ok(()) } } impl ToCss for SyntaxComponent { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { self.kind.to_css(dest)?; match self.multiplier { Multiplier::None => Ok(()), - Multiplier::Comma => dest.write_char('#'), - Multiplier::Space => dest.write_char('+'), + Multiplier::Comma => { + dest.write_char('#')?; + Ok(()) + } + Multiplier::Space => { + dest.write_char('+')?; + Ok(()) + } } } } impl ToCss for SyntaxComponentKind { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { use SyntaxComponentKind::*; let s = match self { Length => "", @@ -459,15 +457,13 @@ impl ToCss for SyntaxComponentKind { CustomIdent => "", Literal(l) => l, }; - dest.write_str(s) + dest.write_str(s)?; + Ok(()) } } impl<'i> ToCss for ParsedComponent<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { use ParsedComponent::*; match self { Length(v) => v.to_css(dest), diff --git a/src/values/time.rs b/src/values/time.rs index 8066bbf5b..46a96f658 100644 --- a/src/values/time.rs +++ b/src/values/time.rs @@ -4,7 +4,6 @@ use super::angle::impl_try_from_angle; use super::calc::Calc; use super::number::{CSSNumber, NumericRange}; use crate::error::{ParserError, PrinterError}; -use crate::printer::Printer; use crate::traits::private::AddInternal; use crate::traits::{impl_op, Map, Op, Parse, ParseNumeric, Sign, ToCss, Zero}; #[cfg(feature = "visitor")] @@ -105,29 +104,30 @@ impl<'i> TryFrom<&Token<'i>> for Time { } impl ToCss for Time { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { // 0.1s is shorter than 100ms // anything smaller is longer match self { Time::Seconds(s) => { if *s > 0.0 && *s < 0.1 { (*s * 1000.0).to_css(dest)?; - dest.write_str("ms") + dest.write_str("ms")?; + Ok(()) } else { s.to_css(dest)?; - dest.write_str("s") + dest.write_str("s")?; + Ok(()) } } Time::Milliseconds(ms) => { if *ms == 0.0 || *ms >= 100.0 { (*ms / 1000.0).to_css(dest)?; - dest.write_str("s") + dest.write_str("s")?; + Ok(()) } else { ms.to_css(dest)?; - dest.write_str("ms") + dest.write_str("ms")?; + Ok(()) } } } diff --git a/src/values/url.rs b/src/values/url.rs index cebdeed9d..0d12ba422 100644 --- a/src/values/url.rs +++ b/src/values/url.rs @@ -2,7 +2,6 @@ use crate::dependencies::{Dependency, Location, UrlDependency}; use crate::error::{ParserError, PrinterError}; -use crate::printer::Printer; use crate::traits::{Parse, ToCss}; use crate::values::string::CowArcStr; #[cfg(feature = "visitor")] @@ -39,11 +38,8 @@ impl<'i> Parse<'i> for Url<'i> { } impl<'i> ToCss for Url<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { - let dep = if dest.dependencies.is_some() { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { + let dep = if dest.state().dependencies.is_some() { Some(UrlDependency::new(self, dest.filename())) } else { None @@ -56,7 +52,7 @@ impl<'i> ToCss for Url<'i> { serialize_string(&dep.placeholder, dest)?; dest.write_char(')')?; - if let Some(dependencies) = &mut dest.dependencies { + if let Some(dependencies) = dest.state_mut().dependencies.as_mut() { dependencies.push(Dependency::Url(dep)) } @@ -64,7 +60,7 @@ impl<'i> ToCss for Url<'i> { } use cssparser::ToCss; - if dest.minify { + if dest.options().minify { let mut buf = String::new(); Token::UnquotedUrl(CowRcStr::from(self.url.as_ref())).to_css(&mut buf)?; @@ -76,7 +72,7 @@ impl<'i> ToCss for Url<'i> { if buf2.len() + 5 < buf.len() { dest.write_str("url(")?; dest.write_str(&buf2)?; - return dest.write_char(')'); + return Ok(dest.write_char(')')?); } } diff --git a/src/vendor_prefix.rs b/src/vendor_prefix.rs index e6353df53..f6f3060f2 100644 --- a/src/vendor_prefix.rs +++ b/src/vendor_prefix.rs @@ -3,7 +3,6 @@ #![allow(non_upper_case_globals)] use crate::error::PrinterError; -use crate::printer::Printer; use crate::traits::ToCss; #[cfg(feature = "visitor")] use crate::visitor::{Visit, VisitTypes, Visitor}; @@ -69,10 +68,7 @@ impl VendorPrefix { } impl ToCss for VendorPrefix { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write, - { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { cssparser::ToCss::to_css(self, dest)?; Ok(()) } @@ -84,10 +80,22 @@ impl cssparser::ToCss for VendorPrefix { W: std::fmt::Write, { match *self { - VendorPrefix::WebKit => dest.write_str("-webkit-"), - VendorPrefix::Moz => dest.write_str("-moz-"), - VendorPrefix::Ms => dest.write_str("-ms-"), - VendorPrefix::O => dest.write_str("-o-"), + VendorPrefix::WebKit => { + dest.write_str("-webkit-")?; + Ok(()) + } + VendorPrefix::Moz => { + dest.write_str("-moz-")?; + Ok(()) + } + VendorPrefix::Ms => { + dest.write_str("-ms-")?; + Ok(()) + } + VendorPrefix::O => { + dest.write_str("-o-")?; + Ok(()) + } _ => Ok(()), } } diff --git a/src/visitor.rs b/src/visitor.rs index 846cd9067..969576cf1 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -54,7 +54,9 @@ //! //! stylesheet.visit(&mut MyVisitor).unwrap(); //! -//! let res = stylesheet.to_css(PrinterOptions { minify: true, ..Default::default() }).unwrap(); +//! let res = stylesheet +//! .to_css(PrinterOptions { minify: true, ..Default::default() }, None::<&mut ()>) +//! .unwrap(); //! assert_eq!(res.code, ".foo{background:url(https://mywebsite.com/bg.png);width:2rem}"); //! ``` diff --git a/tests/test_custom_parser.rs b/tests/test_custom_parser.rs index 6eb0bc903..74cef6d39 100644 --- a/tests/test_custom_parser.rs +++ b/tests/test_custom_parser.rs @@ -1,21 +1,24 @@ use cssparser::*; use rspack_lightningcss::{ - declaration::DeclarationBlock, - error::{ParserError, PrinterError}, - printer::Printer, - stylesheet::{ParserOptions, PrinterOptions, StyleSheet}, - traits::{AtRuleParser, Parse, ToCss}, - values::ident::Ident, + declaration::DeclarationBlock, + error::{ParserError, PrinterError}, + printer::PrinterTrait, + stylesheet::{ParserOptions, PrinterOptions, StyleSheet}, + traits::{AtRuleParser, Parse, ToCss}, + values::ident::Ident, }; fn minify_test(source: &str, expected: &str) { let mut stylesheet = StyleSheet::parse_with(&source, ParserOptions::default(), &mut TestAtRuleParser).unwrap(); stylesheet.minify(Default::default()).unwrap(); let res = stylesheet - .to_css(PrinterOptions { - minify: true, - ..PrinterOptions::default() - }) + .to_css( + PrinterOptions { + minify: true, + ..PrinterOptions::default() + }, + None::<&mut ()>, + ) .unwrap(); assert_eq!(res.code, expected); } @@ -128,7 +131,7 @@ impl<'i> AtRuleParser<'i> for TestAtRuleParser { } impl<'i> ToCss for AtRule<'i> { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { AtRule::Block(rule) => { dest.write_str("@block ")?; @@ -138,7 +141,8 @@ impl<'i> ToCss for AtRule<'i> { AtRule::Inline(rule) => { dest.write_str("@inline ")?; rule.name.to_css(dest)?; - dest.write_char(';') + dest.write_char(';')?; + Ok(()) } } } From 21f7ad3f9c31455da40ea137643077dc3f8bc67f Mon Sep 17 00:00:00 2001 From: AsyncIter Date: Mon, 29 Jun 2026 16:29:26 +0800 Subject: [PATCH 2/9] Refine printer trait call sites --- derive/src/to_css.rs | 2 +- src/properties/align.rs | 96 ++++++++++++--------------------------- src/properties/custom.rs | 3 +- src/properties/display.rs | 54 +++++----------------- src/properties/font.rs | 32 +++++-------- src/properties/grid.rs | 8 +++- src/properties/text.rs | 9 ++-- src/properties/ui.rs | 6 ++- src/selector.rs | 15 ++++-- src/values/color.rs | 14 ++++-- src/values/easing.rs | 48 ++++---------------- src/values/length.rs | 3 +- src/values/url.rs | 3 +- src/vendor_prefix.rs | 25 +++------- 14 files changed, 111 insertions(+), 207 deletions(-) diff --git a/derive/src/to_css.rs b/derive/src/to_css.rs index c9665a45c..c10f39292 100644 --- a/derive/src/to_css.rs +++ b/derive/src/to_css.rs @@ -25,7 +25,7 @@ pub fn derive_to_css(input: TokenStream) -> TokenStream { let output = quote! { impl #impl_generics ToCss for #ident #ty_generics #where_clause { - fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { #imp } } diff --git a/src/properties/align.rs b/src/properties/align.rs index 35f365f0f..bbf3a4ffb 100644 --- a/src/properties/align.rs +++ b/src/properties/align.rs @@ -60,15 +60,10 @@ impl<'i> Parse<'i> for BaselinePosition { impl ToCss for BaselinePosition { fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { - BaselinePosition::First => { - dest.write_str("baseline")?; - Ok(()) - } - BaselinePosition::Last => { - dest.write_str("last baseline")?; - Ok(()) - } - } + BaselinePosition::First => dest.write_str("baseline")?, + BaselinePosition::Last => dest.write_str("last baseline")?, + }; + Ok(()) } } @@ -210,18 +205,15 @@ impl<'i> Parse<'i> for JustifyContent { impl ToCss for JustifyContent { fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { - JustifyContent::Normal => { - dest.write_str("normal")?; - Ok(()) - } - JustifyContent::ContentDistribution(value) => value.to_css(dest), + JustifyContent::Normal => dest.write_str("normal")?, + JustifyContent::ContentDistribution(value) => value.to_css(dest)?, JustifyContent::ContentPosition { overflow, value } => { if let Some(overflow) = overflow { overflow.to_css(dest)?; dest.write_str(" ")?; } - value.to_css(dest) + value.to_css(dest)?; } JustifyContent::Left { overflow } => { if let Some(overflow) = overflow { @@ -230,8 +222,6 @@ impl ToCss for JustifyContent { } dest.write_str("left")?; - - Ok(()) } JustifyContent::Right { overflow } => { if let Some(overflow) = overflow { @@ -240,10 +230,9 @@ impl ToCss for JustifyContent { } dest.write_str("right")?; - - Ok(()) } - } + }; + Ok(()) } } @@ -434,26 +423,17 @@ impl<'i> Parse<'i> for JustifySelf { impl ToCss for JustifySelf { fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { - JustifySelf::Auto => { - dest.write_str("auto")?; - Ok(()) - } - JustifySelf::Normal => { - dest.write_str("normal")?; - Ok(()) - } - JustifySelf::Stretch => { - dest.write_str("stretch")?; - Ok(()) - } - JustifySelf::BaselinePosition(val) => val.to_css(dest), + JustifySelf::Auto => dest.write_str("auto")?, + JustifySelf::Normal => dest.write_str("normal")?, + JustifySelf::Stretch => dest.write_str("stretch")?, + JustifySelf::BaselinePosition(val) => val.to_css(dest)?, JustifySelf::SelfPosition { overflow, value } => { if let Some(overflow) = overflow { overflow.to_css(dest)?; dest.write_str(" ")?; } - value.to_css(dest) + value.to_css(dest)?; } JustifySelf::Left { overflow } => { if let Some(overflow) = overflow { @@ -462,8 +442,6 @@ impl ToCss for JustifySelf { } dest.write_str("left")?; - - Ok(()) } JustifySelf::Right { overflow } => { if let Some(overflow) = overflow { @@ -472,10 +450,9 @@ impl ToCss for JustifySelf { } dest.write_str("right")?; - - Ok(()) } - } + }; + Ok(()) } } @@ -623,19 +600,11 @@ impl ToCss for LegacyJustify { fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { dest.write_str("legacy ")?; match self { - LegacyJustify::Left => { - dest.write_str("left")?; - Ok(()) - } - LegacyJustify::Right => { - dest.write_str("right")?; - Ok(()) - } - LegacyJustify::Center => { - dest.write_str("center")?; - Ok(()) - } - } + LegacyJustify::Left => dest.write_str("left")?, + LegacyJustify::Right => dest.write_str("right")?, + LegacyJustify::Center => dest.write_str("center")?, + }; + Ok(()) } } @@ -717,23 +686,17 @@ impl<'i> Parse<'i> for JustifyItems { impl ToCss for JustifyItems { fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { - JustifyItems::Normal => { - dest.write_str("normal")?; - Ok(()) - } - JustifyItems::Stretch => { - dest.write_str("stretch")?; - Ok(()) - } - JustifyItems::BaselinePosition(val) => val.to_css(dest), - JustifyItems::Legacy(val) => val.to_css(dest), + JustifyItems::Normal => dest.write_str("normal")?, + JustifyItems::Stretch => dest.write_str("stretch")?, + JustifyItems::BaselinePosition(val) => val.to_css(dest)?, + JustifyItems::Legacy(val) => val.to_css(dest)?, JustifyItems::SelfPosition { overflow, value } => { if let Some(overflow) = overflow { overflow.to_css(dest)?; dest.write_str(" ")?; } - value.to_css(dest) + value.to_css(dest)?; } JustifyItems::Left { overflow } => { if let Some(overflow) = overflow { @@ -742,8 +705,6 @@ impl ToCss for JustifyItems { } dest.write_str("left")?; - - Ok(()) } JustifyItems::Right { overflow } => { if let Some(overflow) = overflow { @@ -752,10 +713,9 @@ impl ToCss for JustifyItems { } dest.write_str("right")?; - - Ok(()) } - } + }; + Ok(()) } } diff --git a/src/properties/custom.rs b/src/properties/custom.rs index 3fee0bf6f..2687f29d2 100644 --- a/src/properties/custom.rs +++ b/src/properties/custom.rs @@ -1615,7 +1615,8 @@ impl<'i> UnresolvedColor<'i> { dest.write_str("var(--lightningcss-dark")?; dest.delim(',', false)?; dark.to_css(dest, is_custom_property)?; - return Ok(dest.write_char(')')?); + dest.write_char(')')?; + return Ok(()); } dest.write_str("light-dark(")?; diff --git a/src/properties/display.rs b/src/properties/display.rs index 1c1fdf86a..25c50e8c0 100644 --- a/src/properties/display.rs +++ b/src/properties/display.rs @@ -68,42 +68,25 @@ impl<'i> Parse<'i> for DisplayInside { impl ToCss for DisplayInside { fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { - DisplayInside::Flow => { - dest.write_str("flow")?; - Ok(()) - } - DisplayInside::FlowRoot => { - dest.write_str("flow-root")?; - Ok(()) - } - DisplayInside::Table => { - dest.write_str("table")?; - Ok(()) - } + DisplayInside::Flow => dest.write_str("flow")?, + DisplayInside::FlowRoot => dest.write_str("flow-root")?, + DisplayInside::Table => dest.write_str("table")?, DisplayInside::Flex(prefix) => { prefix.to_css(dest)?; if *prefix == VendorPrefix::Ms { dest.write_str("flexbox")?; - Ok(()) } else { dest.write_str("flex")?; - Ok(()) } } DisplayInside::Box(prefix) => { prefix.to_css(dest)?; dest.write_str("box")?; - Ok(()) - } - DisplayInside::Grid => { - dest.write_str("grid")?; - Ok(()) } - DisplayInside::Ruby => { - dest.write_str("ruby")?; - Ok(()) - } - } + DisplayInside::Grid => dest.write_str("grid")?, + DisplayInside::Ruby => dest.write_str("ruby")?, + }; + Ok(()) } } @@ -246,18 +229,12 @@ impl ToCss for DisplayPair { outside: DisplayOutside::Inline, inside: DisplayInside::FlowRoot, is_list_item: false, - } => { - dest.write_str("inline-block")?; - Ok(()) - } + } => dest.write_str("inline-block")?, DisplayPair { outside: DisplayOutside::Inline, inside: DisplayInside::Table, is_list_item: false, - } => { - dest.write_str("inline-table")?; - Ok(()) - } + } => dest.write_str("inline-table")?, DisplayPair { outside: DisplayOutside::Inline, inside: DisplayInside::Flex(prefix), @@ -266,10 +243,8 @@ impl ToCss for DisplayPair { prefix.to_css(dest)?; if *prefix == VendorPrefix::Ms { dest.write_str("inline-flexbox")?; - Ok(()) } else { dest.write_str("inline-flex")?; - Ok(()) } } DisplayPair { @@ -279,16 +254,12 @@ impl ToCss for DisplayPair { } => { prefix.to_css(dest)?; dest.write_str("inline-box")?; - Ok(()) } DisplayPair { outside: DisplayOutside::Inline, inside: DisplayInside::Grid, is_list_item: false, - } => { - dest.write_str("inline-grid")?; - Ok(()) - } + } => dest.write_str("inline-grid")?, DisplayPair { outside, inside, @@ -319,10 +290,9 @@ impl ToCss for DisplayPair { } dest.write_str("list-item")?; } - - Ok(()) } - } + }; + Ok(()) } } diff --git a/src/properties/font.rs b/src/properties/font.rs index e61d2b727..b6a2b52c3 100644 --- a/src/properties/font.rs +++ b/src/properties/font.rs @@ -83,16 +83,11 @@ impl ToCss for AbsoluteFontWeight { fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { use AbsoluteFontWeight::*; match self { - Weight(val) => val.to_css(dest), - Normal => { - dest.write_str(if dest.options().minify { "400" } else { "normal" })?; - Ok(()) - } - Bold => { - dest.write_str(if dest.options().minify { "700" } else { "bold" })?; - Ok(()) - } - } + Weight(val) => val.to_css(dest)?, + Normal => dest.write_str(if dest.options().minify { "400" } else { "normal" })?, + Bold => dest.write_str(if dest.options().minify { "700" } else { "bold" })?, + }; + Ok(()) } } @@ -432,7 +427,8 @@ impl<'i> ToCss for FamilyName<'i> { serialize_identifier(slice, &mut id)?; } if id.len() < val.len() + 2 { - return Ok(dest.write_str(&id)?); + dest.write_str(&id)?; + return Ok(()); } } serialize_string(&val, dest)?; @@ -502,23 +498,17 @@ impl<'i> Parse<'i> for FontStyle { impl ToCss for FontStyle { fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { - FontStyle::Normal => { - dest.write_str("normal")?; - Ok(()) - } - FontStyle::Italic => { - dest.write_str("italic")?; - Ok(()) - } + FontStyle::Normal => dest.write_str("normal")?, + FontStyle::Italic => dest.write_str("italic")?, FontStyle::Oblique(angle) => { dest.write_str("oblique")?; if *angle != FontStyle::default_oblique_angle() { dest.write_char(' ')?; angle.to_css(dest)?; } - Ok(()) } - } + }; + Ok(()) } } diff --git a/src/properties/grid.rs b/src/properties/grid.rs index efb70fe4b..8020fbbba 100644 --- a/src/properties/grid.rs +++ b/src/properties/grid.rs @@ -401,7 +401,10 @@ fn serialize_line_names( Ok(()) } -fn write_ident(name: &str, dest: &mut PrinterT) -> Result<(), PrinterError> { +fn write_ident( + name: &str, + dest: &mut PrinterT, +) -> Result<(), PrinterError> { let css_module_grid_enabled = dest .state() .css_module @@ -517,7 +520,8 @@ impl<'i> Parse<'i> for TrackSizeList { impl ToCss for TrackSizeList { fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { if self.0.len() == 0 { - return Ok(dest.write_str("auto")?); + dest.write_str("auto")?; + return Ok(()); } let mut first = true; diff --git a/src/properties/text.rs b/src/properties/text.rs index 315e674e5..f7c3e0889 100644 --- a/src/properties/text.rs +++ b/src/properties/text.rs @@ -524,15 +524,18 @@ impl<'i> Parse<'i> for TextDecorationLine { impl ToCss for TextDecorationLine { fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { if self.is_empty() { - return Ok(dest.write_str("none")?); + dest.write_str("none")?; + return Ok(()); } if self.contains(TextDecorationLine::SpellingError) { - return Ok(dest.write_str("spelling-error")?); + dest.write_str("spelling-error")?; + return Ok(()); } if self.contains(TextDecorationLine::GrammarError) { - return Ok(dest.write_str("grammar-error")?); + dest.write_str("grammar-error")?; + return Ok(()); } let mut needs_space = false; diff --git a/src/properties/ui.rs b/src/properties/ui.rs index be2c7ba36..ee79aa956 100644 --- a/src/properties/ui.rs +++ b/src/properties/ui.rs @@ -468,7 +468,8 @@ impl<'i> Parse<'i> for ColorScheme { impl ToCss for ColorScheme { fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { if self.is_empty() { - return Ok(dest.write_str("normal")?); + dest.write_str("normal")?; + return Ok(()); } if self.contains(ColorScheme::Light) { @@ -485,7 +486,8 @@ impl ToCss for ColorScheme { if self.contains(ColorScheme::Only) { // Avoid parsing `color-scheme: only` as `color-scheme: only` if !self.intersects(ColorScheme::Light | ColorScheme::Dark) { - return Ok(dest.write_str("only")?); + dest.write_str("only")?; + return Ok(()); } dest.write_str(" only")?; } diff --git a/src/selector.rs b/src/selector.rs index c2d4d5c54..956592ff6 100644 --- a/src/selector.rs +++ b/src/selector.rs @@ -692,17 +692,20 @@ fn serialize_pseudo_class<'a, 'i, PrinterT: crate::printer::PrinterTrait>( } serialize_identifier(lang, dest)?; } - return Ok(dest.write_str(")")?); + dest.write_str(")")?; + return Ok(()); } Dir { direction: dir } => { dest.write_str(":dir(")?; dir.to_css(dest)?; - return Ok(dest.write_str(")")?); + dest.write_str(")")?; + return Ok(()); } State { state } => { dest.write_str(":state(")?; state.to_css(dest)?; - return Ok(dest.write_str(")")?); + dest.write_str(")")?; + return Ok(()); } _ => {} } @@ -979,7 +982,8 @@ fn serialize_pseudo_class<'a, 'i, PrinterT: crate::printer::PrinterTrait>( Lang { languages: _ } | Dir { direction: _ } | State { .. } => unreachable!(), Custom { name } => { dest.write_char(':')?; - return Ok(dest.write_str(&name)?); + dest.write_str(&name)?; + return Ok(()); } CustomFunction { name, arguments: args } => { dest.write_char(':')?; @@ -1469,7 +1473,8 @@ fn serialize_pseudo_element<'a, 'i, PrinterT: crate::printer::PrinterTrait>( } Custom { name: val } => { dest.write_str("::")?; - return Ok(dest.write_str(val)?); + dest.write_str(val)?; + return Ok(()); } CustomFunction { name, arguments: args } => { dest.write_str("::")?; diff --git a/src/values/color.rs b/src/values/color.rs index 7cecc44a5..43bc91b6f 100644 --- a/src/values/color.rs +++ b/src/values/color.rs @@ -639,7 +639,8 @@ impl ToCss for CssColor { if color.alpha == 255 { let hex: u32 = ((color.red as u32) << 16) | ((color.green as u32) << 8) | (color.blue as u32); if let Some(name) = short_color_name(hex) { - return Ok(dest.write_str(name)?); + dest.write_str(name)?; + return Ok(()); } let compact = compact_hex(hex); @@ -654,7 +655,8 @@ impl ToCss for CssColor { // If the browser doesn't support `#rrggbbaa` color syntax, it is converted to `transparent` when compressed(minify = true). // https://www.w3.org/TR/css-color-4/#transparent-black if dest.options().minify && color.red == 0 && color.green == 0 && color.blue == 0 && color.alpha == 0 { - return Ok(dest.write_str("transparent")?); + dest.write_str("transparent")?; + return Ok(()); } else { dest.write_str("rgba(")?; write!(dest, "{}", color.red)?; @@ -712,7 +714,8 @@ impl ToCss for CssColor { dest.write_str("var(--lightningcss-dark")?; dest.delim(',', false)?; dark.to_css(dest)?; - return Ok(dest.write_char(')')?); + dest.write_char(')')?; + return Ok(()); } dest.write_str("light-dark(")?; @@ -1569,7 +1572,10 @@ fn write_components( } #[inline] -fn write_component(c: f32, dest: &mut PrinterT) -> Result<(), PrinterError> { +fn write_component( + c: f32, + dest: &mut PrinterT, +) -> Result<(), PrinterError> { if c.is_nan() { dest.write_str("none")?; } else { diff --git a/src/values/easing.rs b/src/values/easing.rs index 019987828..8188d8587 100644 --- a/src/values/easing.rs +++ b/src/values/easing.rs @@ -110,30 +110,12 @@ impl<'i> Parse<'i> for EasingFunction { impl ToCss for EasingFunction { fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { - EasingFunction::Linear => { - dest.write_str("linear")?; - Ok(()) - } - EasingFunction::Ease => { - dest.write_str("ease")?; - Ok(()) - } - EasingFunction::EaseIn => { - dest.write_str("ease-in")?; - Ok(()) - } - EasingFunction::EaseOut => { - dest.write_str("ease-out")?; - Ok(()) - } - EasingFunction::EaseInOut => { - dest.write_str("ease-in-out")?; - Ok(()) - } - _ if self.is_ease() => { - dest.write_str("ease")?; - Ok(()) - } + EasingFunction::Linear => dest.write_str("linear")?, + EasingFunction::Ease => dest.write_str("ease")?, + EasingFunction::EaseIn => dest.write_str("ease-in")?, + EasingFunction::EaseOut => dest.write_str("ease-out")?, + EasingFunction::EaseInOut => dest.write_str("ease-in-out")?, + _ if self.is_ease() => dest.write_str("ease")?, x if *x == EasingFunction::CubicBezier { x1: 0.42, @@ -143,7 +125,6 @@ impl ToCss for EasingFunction { } => { dest.write_str("ease-in")?; - Ok(()) } x if *x == EasingFunction::CubicBezier { @@ -154,7 +135,6 @@ impl ToCss for EasingFunction { } => { dest.write_str("ease-out")?; - Ok(()) } x if *x == EasingFunction::CubicBezier { @@ -165,7 +145,6 @@ impl ToCss for EasingFunction { } => { dest.write_str("ease-in-out")?; - Ok(()) } EasingFunction::CubicBezier { x1, y1, x2, y2 } => { dest.write_str("cubic-bezier(")?; @@ -177,31 +156,24 @@ impl ToCss for EasingFunction { dest.delim(',', false)?; y2.to_css(dest)?; dest.write_char(')')?; - Ok(()) } EasingFunction::Steps { count: 1, position: StepPosition::Start, - } => { - dest.write_str("step-start")?; - Ok(()) - } + } => dest.write_str("step-start")?, EasingFunction::Steps { count: 1, position: StepPosition::End, - } => { - dest.write_str("step-end")?; - Ok(()) - } + } => dest.write_str("step-end")?, EasingFunction::Steps { count, position } => { dest.write_str("steps(")?; write!(dest, "{}", count)?; dest.delim(',', false)?; position.to_css(dest)?; dest.write_char(')')?; - Ok(()) } - } + }; + Ok(()) } } diff --git a/src/values/length.rs b/src/values/length.rs index e0e1d8d0d..8e6dac490 100644 --- a/src/values/length.rs +++ b/src/values/length.rs @@ -487,7 +487,8 @@ impl ToCss for LengthValue { // The unit can be omitted if the value is zero, except inside calc() // expressions, where unitless numbers won't be parsed as dimensions. if !dest.state().in_calc && value == 0.0 { - return Ok(dest.write_char('0')?); + dest.write_char('0')?; + return Ok(()); } serialize_dimension(value, unit, dest) diff --git a/src/values/url.rs b/src/values/url.rs index 0d12ba422..ac88356db 100644 --- a/src/values/url.rs +++ b/src/values/url.rs @@ -72,7 +72,8 @@ impl<'i> ToCss for Url<'i> { if buf2.len() + 5 < buf.len() { dest.write_str("url(")?; dest.write_str(&buf2)?; - return Ok(dest.write_char(')')?); + dest.write_char(')')?; + return Ok(()); } } diff --git a/src/vendor_prefix.rs b/src/vendor_prefix.rs index f6f3060f2..3c672a124 100644 --- a/src/vendor_prefix.rs +++ b/src/vendor_prefix.rs @@ -80,24 +80,13 @@ impl cssparser::ToCss for VendorPrefix { W: std::fmt::Write, { match *self { - VendorPrefix::WebKit => { - dest.write_str("-webkit-")?; - Ok(()) - } - VendorPrefix::Moz => { - dest.write_str("-moz-")?; - Ok(()) - } - VendorPrefix::Ms => { - dest.write_str("-ms-")?; - Ok(()) - } - VendorPrefix::O => { - dest.write_str("-o-")?; - Ok(()) - } - _ => Ok(()), - } + VendorPrefix::WebKit => dest.write_str("-webkit-")?, + VendorPrefix::Moz => dest.write_str("-moz-")?, + VendorPrefix::Ms => dest.write_str("-ms-")?, + VendorPrefix::O => dest.write_str("-o-")?, + _ => {} + }; + Ok(()) } } From e93d6d928b5aa38c6edf1598c6232cc74766a8d9 Mon Sep 17 00:00:00 2001 From: AsyncIter Date: Mon, 29 Jun 2026 17:45:11 +0800 Subject: [PATCH 3/9] Move source maps into printer options --- src/bundler.rs | 28 ++--- src/declaration.rs | 2 +- src/lib.rs | 196 +++++++++++++------------------ src/main.rs | 4 +- src/printer.rs | 116 ++++++++++++++---- src/properties/background.rs | 2 +- src/properties/custom.rs | 4 +- src/properties/font.rs | 6 +- src/properties/grid.rs | 16 +-- src/properties/transform.rs | 44 ++++--- src/rules/font_face.rs | 2 +- src/rules/font_feature_values.rs | 2 +- src/rules/font_palette_values.rs | 2 +- src/rules/keyframes.rs | 8 +- src/rules/media.rs | 2 +- src/rules/mod.rs | 4 +- src/rules/page.rs | 8 +- src/rules/property.rs | 2 +- src/rules/scope.rs | 2 +- src/rules/style.rs | 11 +- src/rules/view_transition.rs | 2 +- src/selector.rs | 6 +- src/stylesheet.rs | 27 +---- src/traits.rs | 2 +- src/values/color.rs | 2 +- src/values/gradient.rs | 10 +- src/values/position.rs | 2 +- src/values/url.rs | 2 +- src/visitor.rs | 2 +- 29 files changed, 258 insertions(+), 258 deletions(-) diff --git a/src/bundler.rs b/src/bundler.rs index 717bf6109..dce46a50e 100644 --- a/src/bundler.rs +++ b/src/bundler.rs @@ -958,7 +958,7 @@ mod tests { fn bundle(fs: P, entry: &str) -> String { let mut bundler = Bundler::<_, _, ()>::new(&fs, None, ParserOptions::default()); let stylesheet = bundler.bundle(Path::new(entry)).unwrap(); - stylesheet.to_css(PrinterOptions::default(), None::<&mut ()>).unwrap().code + stylesheet.to_css(PrinterOptions::default()).unwrap().code } fn bundle_css_module( @@ -990,13 +990,10 @@ mod tests { let mut stylesheet = bundler.bundle(Path::new(entry)).unwrap(); stylesheet.minify(MinifyOptions::default()).unwrap(); let res = stylesheet - .to_css( - PrinterOptions { - project_root, - ..PrinterOptions::default() - }, - None::<&mut ()>, - ) + .to_css(PrinterOptions { + project_root, + ..PrinterOptions::default() + }) .unwrap(); (res.code, res.exports.unwrap()) } @@ -1025,13 +1022,10 @@ mod tests { }) .unwrap(); stylesheet - .to_css( - PrinterOptions { - targets, - ..PrinterOptions::default() - }, - None::<&mut ()>, - ) + .to_css(PrinterOptions { + targets, + ..PrinterOptions::default() + }) .unwrap() .code } @@ -2204,8 +2198,8 @@ mod tests { PrinterOptions { minify: true, ..PrinterOptions::default() - }, - Some(&mut sm), + } + .with_source_map(Some(&mut sm)), ) .unwrap(); let map = sm.to_json(None).unwrap(); diff --git a/src/declaration.rs b/src/declaration.rs index 36f30cacd..7c158792b 100644 --- a/src/declaration.rs +++ b/src/declaration.rs @@ -213,7 +213,7 @@ impl<'i> DeclarationBlock<'i> { } decl.to_css(dest, $important)?; - if i != len - 1 || !dest.options().minify || has_nested_rules { + if i != len - 1 || !dest.minify() || has_nested_rules { dest.write_char(';')?; } diff --git a/src/lib.rs b/src/lib.rs index 262ccdda1..358f18068 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -138,7 +138,7 @@ mod tests { if let Err(e) = stylesheet.minify(MinifyOptions::default()) { panic_with_test_error("test_with_options", "minify", source, e); } - let res = match stylesheet.to_css(PrinterOptions::default(), None::<&mut ()>) { + let res = match stylesheet.to_css(PrinterOptions::default()) { Ok(res) => res, Err(e) => panic_with_test_error("test_with_options", "print", source, e), }; @@ -154,7 +154,7 @@ mod tests { if let Err(e) = stylesheet.minify(MinifyOptions::default()) { panic_with_test_error("test_with_printer_options", "minify", source, e); } - let res = match stylesheet.to_css(options, None::<&mut ()>) { + let res = match stylesheet.to_css(options) { Ok(res) => res, Err(e) => panic_with_test_error("test_with_printer_options", "print", source, e), }; @@ -189,14 +189,11 @@ mod tests { }) { panic_with_test_error("minify_test_with_options", "minify", source, e); } - let res = match stylesheet.to_css( - PrinterOptions { - minify: true, - targets, - ..PrinterOptions::default() - }, - None::<&mut ()>, - ) { + let res = match stylesheet.to_css(PrinterOptions { + minify: true, + targets, + ..PrinterOptions::default() + }) { Ok(res) => res, Err(e) => panic_with_test_error("minify_test_with_options", "print", source, e), }; @@ -230,13 +227,10 @@ mod tests { }) { panic_with_test_error("prefix_test", "minify", source, e); } - let res = match stylesheet.to_css( - PrinterOptions { - targets: targets.into(), - ..PrinterOptions::default() - }, - None::<&mut ()>, - ) { + let res = match stylesheet.to_css(PrinterOptions { + targets: targets.into(), + ..PrinterOptions::default() + }) { Ok(res) => res, Err(e) => panic_with_test_error("prefix_test", "print", source, e), }; @@ -289,13 +283,10 @@ mod tests { }) { panic_with_test_error("nesting_test_with_targets", "minify", source, e); } - let res = match stylesheet.to_css( - PrinterOptions { - targets, - ..PrinterOptions::default() - }, - None::<&mut ()>, - ) { + let res = match stylesheet.to_css(PrinterOptions { + targets, + ..PrinterOptions::default() + }) { Ok(res) => res, Err(e) => panic_with_test_error("nesting_test_with_targets", "print", source, e), }; @@ -311,7 +302,7 @@ mod tests { if let Err(e) = stylesheet.minify(MinifyOptions::default()) { panic_with_test_error("nesting_test_no_targets", "minify", source, e); } - let res = match stylesheet.to_css(PrinterOptions::default(), None::<&mut ()>) { + let res = match stylesheet.to_css(PrinterOptions::default()) { Ok(res) => res, Err(e) => panic_with_test_error("nesting_test_no_targets", "print", source, e), }; @@ -376,13 +367,10 @@ mod tests { if let Err(e) = stylesheet.minify(MinifyOptions::default()) { panic_with_test_error("css_modules_test", "minify", source, e); } - let res = match stylesheet.to_css( - PrinterOptions { - minify, - ..Default::default() - }, - None::<&mut ()>, - ) { + let res = match stylesheet.to_css(PrinterOptions { + minify, + ..Default::default() + }) { Ok(res) => res, Err(e) => panic_with_test_error("css_modules_test", "print", source, e), }; @@ -419,7 +407,7 @@ mod tests { }) { panic_with_test_error("custom_media_test", "minify", source, e); } - let res = match stylesheet.to_css(PrinterOptions::default(), None::<&mut ()>) { + let res = match stylesheet.to_css(PrinterOptions::default()) { Ok(res) => res, Err(e) => panic_with_test_error("custom_media_test", "print", source, e), }; @@ -27034,13 +27022,10 @@ mod tests { .unwrap(); stylesheet.minify(MinifyOptions::default()).unwrap(); let res = stylesheet - .to_css( - PrinterOptions { - minify: true, - ..PrinterOptions::default() - }, - None::<&mut ()>, - ) + .to_css(PrinterOptions { + minify: true, + ..PrinterOptions::default() + }) .unwrap(); assert_eq!(res.code, ".foo{color:#00f;& .bar{color:red}}"); @@ -27744,7 +27729,7 @@ mod tests { }, ) .unwrap(); - if let Err(err) = stylesheet.to_css(PrinterOptions::default(), None::<&mut ()>) { + if let Err(err) = stylesheet.to_css(PrinterOptions::default()) { assert_eq!(err.kind, PrinterErrorKind::InvalidCssModulesPatternInGrid); } else { unreachable!() @@ -28202,13 +28187,10 @@ mod tests { ) .unwrap(); let res = stylesheet - .to_css( - PrinterOptions { - project_root: Some(project_root), - ..PrinterOptions::default() - }, - None::<&mut ()>, - ) + .to_css(PrinterOptions { + project_root: Some(project_root), + ..PrinterOptions::default() + }) .unwrap(); assert_eq!( res.code, @@ -28247,17 +28229,14 @@ mod tests { .unwrap(); stylesheet.minify(MinifyOptions::default()).unwrap(); let res = stylesheet - .to_css( - PrinterOptions { - targets: Browsers { - chrome: Some(95 << 16), - ..Browsers::default() - } - .into(), - ..Default::default() - }, - None::<&mut ()>, - ) + .to_css(PrinterOptions { + targets: Browsers { + chrome: Some(95 << 16), + ..Browsers::default() + } + .into(), + ..Default::default() + }) .unwrap(); assert_eq!( res.code, @@ -28314,18 +28293,15 @@ mod tests { let stylesheet = StyleSheet::parse(&source, ParserOptions::default()).unwrap(); let res = stylesheet - .to_css( - PrinterOptions { - pseudo_classes: Some(PseudoClasses { - hover: Some("is-hovered"), - active: Some("is-active"), - focus_visible: Some("focus-visible"), - ..PseudoClasses::default() - }), - ..PrinterOptions::default() - }, - None::<&mut ()>, - ) + .to_css(PrinterOptions { + pseudo_classes: Some(PseudoClasses { + hover: Some("is-hovered"), + active: Some("is-active"), + focus_visible: Some("focus-visible"), + ..PseudoClasses::default() + }), + ..PrinterOptions::default() + }) .unwrap(); assert_eq!(res.code, expected); @@ -28351,16 +28327,13 @@ mod tests { ) .unwrap(); let res = stylesheet - .to_css( - PrinterOptions { - pseudo_classes: Some(PseudoClasses { - hover: Some("is-hovered"), - ..PseudoClasses::default() - }), - ..PrinterOptions::default() - }, - None::<&mut ()>, - ) + .to_css(PrinterOptions { + pseudo_classes: Some(PseudoClasses { + hover: Some("is-hovered"), + ..PseudoClasses::default() + }), + ..PrinterOptions::default() + }) .unwrap(); assert_eq!(res.code, expected); } @@ -28441,7 +28414,7 @@ mod tests { ..MinifyOptions::default() }) .unwrap(); - let res = stylesheet.to_css(PrinterOptions::default(), None::<&mut ()>).unwrap(); + let res = stylesheet.to_css(PrinterOptions::default()).unwrap(); assert_eq!(res.code, expected); let source = r#" @@ -28467,7 +28440,7 @@ mod tests { ..MinifyOptions::default() }) .unwrap(); - let res = stylesheet.to_css(PrinterOptions::default(), None::<&mut ()>).unwrap(); + let res = stylesheet.to_css(PrinterOptions::default()).unwrap(); assert_eq!(res.code, expected); let source = r#" @@ -28514,17 +28487,14 @@ mod tests { }) .unwrap(); let res = stylesheet - .to_css( - PrinterOptions { - targets: Browsers { - chrome: Some(95 << 16), - ..Browsers::default() - } - .into(), - ..PrinterOptions::default() - }, - None::<&mut ()>, - ) + .to_css(PrinterOptions { + targets: Browsers { + chrome: Some(95 << 16), + ..Browsers::default() + } + .into(), + ..PrinterOptions::default() + }) .unwrap(); assert_eq!(res.code, expected); @@ -28566,7 +28536,7 @@ mod tests { ..MinifyOptions::default() }) .unwrap(); - let res = stylesheet.to_css(PrinterOptions::default(), None::<&mut ()>).unwrap(); + let res = stylesheet.to_css(PrinterOptions::default()).unwrap(); assert_eq!(res.code, expected); } @@ -30410,14 +30380,11 @@ mod tests { .unwrap(); stylesheet.minify(MinifyOptions::default()).unwrap(); let res = stylesheet - .to_css( - PrinterOptions { - analyze_dependencies: Some(Default::default()), - minify: true, - ..PrinterOptions::default() - }, - None::<&mut ()>, - ) + .to_css(PrinterOptions { + analyze_dependencies: Some(Default::default()), + minify: true, + ..PrinterOptions::default() + }) .unwrap(); assert_eq!(res.code, expected); let dependencies = res.dependencies.unwrap(); @@ -30438,13 +30405,10 @@ mod tests { fn dep_error_test(source: &str, error: PrinterErrorKind) { let stylesheet = StyleSheet::parse(&source, ParserOptions::default()).unwrap(); - let res = stylesheet.to_css( - PrinterOptions { - analyze_dependencies: Some(Default::default()), - ..PrinterOptions::default() - }, - None::<&mut ()>, - ); + let res = stylesheet.to_css(PrinterOptions { + analyze_dependencies: Some(Default::default()), + ..PrinterOptions::default() + }); match res { Err(e) => assert_eq!(e.kind, error), _ => unreachable!(), @@ -31269,8 +31233,8 @@ mod tests { PrinterOptions { minify: true, ..PrinterOptions::default() - }, - Some(&mut source_map), + } + .with_source_map(Some(&mut source_map)), ) .unwrap(); @@ -31316,8 +31280,8 @@ mod tests { PrinterOptions { minify: true, ..PrinterOptions::default() - }, - Some(&mut sm), + } + .with_source_map(Some(&mut sm)), ) .unwrap(); let map = sm.to_json(None).unwrap(); @@ -31364,8 +31328,8 @@ mod tests { PrinterOptions { minify: true, ..PrinterOptions::default() - }, - Some(&mut sm), + } + .with_source_map(Some(&mut sm)), ) .unwrap(); let map = sm.to_json(None).unwrap(); diff --git a/src/main.rs b/src/main.rs index 27f9c8a51..1ae9feadb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -219,8 +219,8 @@ pub fn main() -> Result<(), std::io::Error> { project_root: Some(&project_root.to_string_lossy()), targets, ..PrinterOptions::default() - }, - source_map.as_mut(), + } + .with_source_map(source_map.as_mut()), ) .unwrap() }; diff --git a/src/printer.rs b/src/printer.rs index 685bcae80..da53f653e 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -205,9 +205,12 @@ impl SourceMap for ParcelSourceMap { } /// Options that control how CSS is serialized to a string. -pub struct PrinterOptions<'a> { +pub struct PrinterOptions<'a, S: SourceMap = ()> { /// Whether to minify the CSS, i.e. remove white space. pub minify: bool, + /// An optional reference to a source map to write mappings into. + #[cfg_attr(docsrs, doc(cfg(feature = "custom_sourcemap")))] + pub source_map: Option<&'a mut S>, /// An optional project root path, used to generate relative paths for sources used in CSS module hashes. pub project_root: Option<&'a str>, /// Targets to output the CSS for. @@ -229,6 +232,7 @@ impl<'a> Default for PrinterOptions<'a> { fn default() -> Self { PrinterOptions { minify: false, + source_map: None, project_root: None, targets: Targets::default(), analyze_dependencies: None, @@ -237,6 +241,59 @@ impl<'a> Default for PrinterOptions<'a> { } } +impl<'a, S: SourceMap> PrinterOptions<'a, S> { + /// Returns these options with a source map writer attached. + pub fn with_source_map(self, source_map: Option<&'a mut T>) -> PrinterOptions<'a, T> { + PrinterOptions { + minify: self.minify, + source_map, + project_root: self.project_root, + targets: self.targets, + analyze_dependencies: self.analyze_dependencies, + pseudo_classes: self.pseudo_classes, + } + } +} + +/// Serialization options exposed to CSS printers. +/// +/// Source map storage is intentionally excluded so printing code can depend on +/// configuration without depending on a concrete source map implementation. +pub trait PrinterOptionsTrait { + /// Whether to minify the CSS, i.e. remove white space. + fn minify(&self) -> bool; + /// An optional project root path, used to generate relative paths for sources used in CSS module hashes. + fn project_root(&self) -> Option<&str>; + /// Targets to output the CSS for. + fn targets(&self) -> Targets; + /// Whether to analyze dependencies (i.e. `@import` and `url()`). + fn analyze_dependencies(&self) -> Option<&DependencyOptions>; + /// A mapping of pseudo classes to replace with class names. + fn pseudo_classes(&self) -> Option<&PseudoClasses<'_>>; +} + +impl PrinterOptionsTrait for PrinterOptions<'_, S> { + fn minify(&self) -> bool { + self.minify + } + + fn project_root(&self) -> Option<&str> { + self.project_root + } + + fn targets(&self) -> Targets { + self.targets + } + + fn analyze_dependencies(&self) -> Option<&DependencyOptions> { + self.analyze_dependencies.as_ref() + } + + fn pseudo_classes(&self) -> Option<&PseudoClasses<'_>> { + self.pseudo_classes.as_ref() + } +} + #[allow(missing_docs)] pub struct PrinterState<'a, 'c> { pub(crate) loc: Location, @@ -281,7 +338,7 @@ pub struct PseudoClasses<'a> { pub struct Printer<'a, 'c, W, S: SourceMap = ()> { pub(crate) sources: Option<&'c Vec>, dest: &'a mut W, - pub(crate) options: PrinterOptions<'a>, + pub(crate) options: PrinterOptions<'a, S>, pub(crate) state: PrinterState<'a, 'c>, #[cfg(feature = "custom_sourcemap")] pub(crate) source_map: Option<&'a mut S>, @@ -299,18 +356,30 @@ impl<'a, 'c, W: std::fmt::Write + Sized> Printer<'a, 'c, W, ()> { } impl<'a, 'c, W: std::fmt::Write + Sized, S: SourceMap> Printer<'a, 'c, W, S> { - fn new_impl(dest: &'a mut W, options: PrinterOptions<'a>) -> Self { - let dependencies = if options.analyze_dependencies.is_some() { + /// Create a new Printer wrapping the given destination and options. + pub fn new_with_options(dest: &'a mut W, options: PrinterOptions<'a, S>) -> Self { + Self::new_impl(dest, options) + } + + fn new_impl( + dest: &'a mut W, + #[cfg_attr(not(feature = "custom_sourcemap"), allow(unused_mut))] mut options: PrinterOptions<'a, S>, + ) -> Self { + let dependencies = if options.analyze_dependencies().is_some() { Some(Vec::new()) } else { None }; - let remove_imports = matches!(&options.analyze_dependencies, Some(d) if d.remove_imports); + let remove_imports = matches!(options.analyze_dependencies(), Some(d) if d.remove_imports); + #[cfg(feature = "custom_sourcemap")] + let mut source_map = None; + #[cfg(feature = "custom_sourcemap")] + std::mem::swap(&mut source_map, &mut options.source_map); Printer { sources: None, dest, #[cfg(feature = "custom_sourcemap")] - source_map: None, + source_map, state: PrinterState { loc: Location { source_index: 0, @@ -320,7 +389,7 @@ impl<'a, 'c, W: std::fmt::Write + Sized, S: SourceMap> Printer<'a, 'c, W, S> { generated_line: 0, generated_col: 0, indent: 0, - targets: TargetsWithSupportsScope::new(options.targets), + targets: TargetsWithSupportsScope::new(options.targets()), vendor_prefix: VendorPrefix::empty(), in_calc: false, css_module: None, @@ -336,19 +405,6 @@ impl<'a, 'c, W: std::fmt::Write + Sized, S: SourceMap> Printer<'a, 'c, W, S> { } } - #[cfg(feature = "custom_sourcemap")] - /// Attaches a source map writer to this printer. - pub fn with_source_map(self, source_map: Option<&'a mut T>) -> Printer<'a, 'c, W, T> { - Printer { - sources: self.sources, - dest: self.dest, - options: self.options, - state: self.state, - source_map, - source_maps: Vec::new(), - } - } - /// Returns the current source filename that is being printed. pub fn filename(&self) -> &'c str { if let Some(sources) = self.sources { @@ -408,7 +464,7 @@ impl<'a, 'c, W: std::fmt::Write + Sized, S: SourceMap> Printer<'a, 'c, W, S> { /// Use `write_char` instead if you wish to force a space character to be written, /// regardless of the `minify` option. pub fn whitespace(&mut self) -> Result<(), PrinterError> { - if self.options.minify { + if self.options.minify() { return Ok(()); } @@ -428,7 +484,7 @@ impl<'a, 'c, W: std::fmt::Write + Sized, S: SourceMap> Printer<'a, 'c, W, S> { /// Writes a newline character followed by indentation. /// If the `minify` option is enabled, then nothing is printed. pub fn newline(&mut self) -> Result<(), PrinterError> { - if self.options.minify { + if self.options.minify() { return Ok(()); } @@ -606,9 +662,17 @@ mod private { /// A printer abstraction used by CSS serialization implementations. #[allow(missing_docs)] pub trait PrinterTrait: std::fmt::Write + private::Sealed { - fn options(&self) -> &PrinterOptions<'_>; + type Options: PrinterOptionsTrait; + + fn options(&self) -> &Self::Options; fn state(&self) -> &PrinterState<'_, '_>; fn state_mut(&mut self) -> &mut PrinterState<'_, '_>; + fn minify(&self) -> bool { + self.options().minify() + } + fn pseudo_classes(&self) -> Option<&PseudoClasses<'_>> { + self.options().pseudo_classes() + } fn filename(&self) -> &str; fn error(&self, kind: PrinterErrorKind, loc: crate::dependencies::Location) -> Error { Error { @@ -639,8 +703,10 @@ pub trait PrinterTrait: std::fmt::Write + private::Sealed { impl<'a, 'c, W: std::fmt::Write + Sized, S: SourceMap> private::Sealed for Printer<'a, 'c, W, S> {} impl<'a, 'c, W: std::fmt::Write + Sized, S: SourceMap> PrinterTrait for Printer<'a, 'c, W, S> { - fn options(&self) -> &PrinterOptions<'_> { - unsafe { std::mem::transmute::<&PrinterOptions<'a>, &PrinterOptions<'_>>(&self.options) } + type Options = PrinterOptions<'a, S>; + + fn options(&self) -> &Self::Options { + &self.options } fn state(&self) -> &PrinterState<'_, '_> { diff --git a/src/properties/background.rs b/src/properties/background.rs index b95a2f37a..c4a802742 100644 --- a/src/properties/background.rs +++ b/src/properties/background.rs @@ -515,7 +515,7 @@ impl<'i> ToCss for Background<'i> { // If nothing was output, then this is the initial value, e.g. background: transparent if !has_output { - if dest.options().minify { + if dest.minify() { // `0 0` is the shortest valid background value self.position.to_css(dest)?; } else { diff --git a/src/properties/custom.rs b/src/properties/custom.rs index 2687f29d2..ab09af17f 100644 --- a/src/properties/custom.rs +++ b/src/properties/custom.rs @@ -885,14 +885,14 @@ impl<'a> ToCss for Token<'a> { } .to_css(dest)?, Token::WhiteSpace(w) => { - if dest.options().minify { + if dest.minify() { dest.write_char(' ')?; } else { dest.write_str(&w)?; } } Token::Comment(c) => { - if !dest.options().minify { + if !dest.minify() { cssparser::Token::Comment(c).to_css(dest)?; } } diff --git a/src/properties/font.rs b/src/properties/font.rs index b6a2b52c3..fa0ec202c 100644 --- a/src/properties/font.rs +++ b/src/properties/font.rs @@ -84,8 +84,8 @@ impl ToCss for AbsoluteFontWeight { use AbsoluteFontWeight::*; match self { Weight(val) => val.to_css(dest)?, - Normal => dest.write_str(if dest.options().minify { "400" } else { "normal" })?, - Bold => dest.write_str(if dest.options().minify { "700" } else { "bold" })?, + Normal => dest.write_str(if dest.minify() { "400" } else { "normal" })?, + Bold => dest.write_str(if dest.minify() { "700" } else { "bold" })?, }; Ok(()) } @@ -260,7 +260,7 @@ impl Into for &FontStretch { impl ToCss for FontStretch { fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { - if dest.options().minify { + if dest.minify() { let percentage: Percentage = self.into(); return percentage.to_css(dest); } diff --git a/src/properties/grid.rs b/src/properties/grid.rs index 8020fbbba..b76b45a4c 100644 --- a/src/properties/grid.rs +++ b/src/properties/grid.rs @@ -641,7 +641,7 @@ impl ToCss for GridTemplateAreas { let mut next = iter.next(); let mut first = true; while next.is_some() { - if !first && !dest.options().minify { + if !first && !dest.minify() { dest.newline()?; } @@ -649,14 +649,14 @@ impl ToCss for GridTemplateAreas { if first { first = false; - if !dest.options().minify { + if !dest.minify() { // Indent by the width of "grid-template-areas: ", so the rows line up. dest.indent_by(21); } } } - if !dest.options().minify { + if !dest.minify() { dest.dedent_by(21); } @@ -684,13 +684,13 @@ impl GridTemplateAreas { for i in 0..columns { if let Some(token) = next { if let Some(string) = token { - if i > 0 && (!last_was_null || !dest.options().minify) { + if i > 0 && (!last_was_null || !dest.minify()) { dest.write_char(' ')?; } write_ident(string, dest)?; last_was_null = false; } else { - if i > 0 && (last_was_null || !dest.options().minify) { + if i > 0 && (last_was_null || !dest.minify()) { dest.write_char(' ')?; } dest.write_char('.')?; @@ -845,7 +845,7 @@ impl GridTemplate<'_> { while next.is_some() { macro_rules! newline { () => { - if !dest.options().minify { + if !dest.minify() { if !indented { // Indent by the width of "grid-template: ", so the rows line up. dest.indent_by(indent); @@ -858,7 +858,7 @@ impl GridTemplate<'_> { if let Some(line_names) = line_names_iter.next() { if !line_names.is_empty() { - if !dest.options().minify && line_names.len() == 2 { + if !dest.minify() && line_names.len() == 2 { dest.whitespace()?; serialize_line_names(&line_names[0..1], dest)?; newline!(); @@ -1088,7 +1088,7 @@ impl ToCss for GridAutoFlow { } else if *self == GridAutoFlow::Column { "column" } else if *self == GridAutoFlow::Row | GridAutoFlow::Dense { - if dest.options().minify { + if dest.minify() { "dense" } else { "row dense" diff --git a/src/properties/transform.rs b/src/properties/transform.rs index 2b1f67f78..750aa9f3f 100644 --- a/src/properties/transform.rs +++ b/src/properties/transform.rs @@ -55,7 +55,7 @@ impl ToCss for TransformList { // TODO: Re-enable with a better solution // See: https://github.com/parcel-bundler/lightningcss/issues/288 - // if dest.options().minify { + // if dest.minify() { // // Combine transforms into a single matrix. // if let Some(matrix) = self.to_matrix() { // // Generate based on the original transforms. @@ -1020,7 +1020,7 @@ impl ToCss for Transform { use Transform::*; match self { Translate(x, y) => { - if dest.options().minify && x.is_zero() && !y.is_zero() { + if dest.minify() && x.is_zero() && !y.is_zero() { dest.write_str("translateY(")?; y.to_css(dest)? } else { @@ -1035,11 +1035,7 @@ impl ToCss for Transform { Ok(()) } TranslateX(x) => { - dest.write_str(if dest.options().minify { - "translate(" - } else { - "translateX(" - })?; + dest.write_str(if dest.minify() { "translate(" } else { "translateX(" })?; x.to_css(dest)?; dest.write_char(')')?; Ok(()) @@ -1057,16 +1053,16 @@ impl ToCss for Transform { Ok(()) } Translate3d(x, y, z) => { - if dest.options().minify && !x.is_zero() && y.is_zero() && z.is_zero() { + if dest.minify() && !x.is_zero() && y.is_zero() && z.is_zero() { dest.write_str("translate(")?; x.to_css(dest)?; - } else if dest.options().minify && x.is_zero() && !y.is_zero() && z.is_zero() { + } else if dest.minify() && x.is_zero() && !y.is_zero() && z.is_zero() { dest.write_str("translateY(")?; y.to_css(dest)?; - } else if dest.options().minify && x.is_zero() && y.is_zero() && !z.is_zero() { + } else if dest.minify() && x.is_zero() && y.is_zero() && !z.is_zero() { dest.write_str("translateZ(")?; z.to_css(dest)?; - } else if dest.options().minify && z.is_zero() { + } else if dest.minify() && z.is_zero() { dest.write_str("translate(")?; x.to_css(dest)?; dest.delim(',', false)?; @@ -1085,10 +1081,10 @@ impl ToCss for Transform { Scale(x, y) => { let x: f32 = x.into(); let y: f32 = y.into(); - if dest.options().minify && x == 1.0 && y != 1.0 { + if dest.minify() && x == 1.0 && y != 1.0 { dest.write_str("scaleY(")?; y.to_css(dest)?; - } else if dest.options().minify && x != 1.0 && y == 1.0 { + } else if dest.minify() && x != 1.0 && y == 1.0 { dest.write_str("scaleX(")?; x.to_css(dest)?; } else { @@ -1127,23 +1123,23 @@ impl ToCss for Transform { let x: f32 = x.into(); let y: f32 = y.into(); let z: f32 = z.into(); - if dest.options().minify && z == 1.0 && x == y { + if dest.minify() && z == 1.0 && x == y { // scale3d(x, x, 1) => scale(x) dest.write_str("scale(")?; x.to_css(dest)?; - } else if dest.options().minify && x != 1.0 && y == 1.0 && z == 1.0 { + } else if dest.minify() && x != 1.0 && y == 1.0 && z == 1.0 { // scale3d(x, 1, 1) => scaleX(x) dest.write_str("scaleX(")?; x.to_css(dest)?; - } else if dest.options().minify && x == 1.0 && y != 1.0 && z == 1.0 { + } else if dest.minify() && x == 1.0 && y != 1.0 && z == 1.0 { // scale3d(1, y, 1) => scaleY(y) dest.write_str("scaleY(")?; y.to_css(dest)?; - } else if dest.options().minify && x == 1.0 && y == 1.0 && z != 1.0 { + } else if dest.minify() && x == 1.0 && y == 1.0 && z != 1.0 { // scale3d(1, 1, z) => scaleZ(z) dest.write_str("scaleZ(")?; z.to_css(dest)?; - } else if dest.options().minify && z == 1.0 { + } else if dest.minify() && z == 1.0 { // scale3d(x, y, 1) => scale(x, y) dest.write_str("scale(")?; x.to_css(dest)?; @@ -1179,21 +1175,21 @@ impl ToCss for Transform { Ok(()) } RotateZ(angle) => { - dest.write_str(if dest.options().minify { "rotate(" } else { "rotateZ(" })?; + dest.write_str(if dest.minify() { "rotate(" } else { "rotateZ(" })?; angle.to_css_with_unitless_zero(dest)?; dest.write_char(')')?; Ok(()) } Rotate3d(x, y, z, angle) => { - if dest.options().minify && *x == 1.0 && *y == 0.0 && *z == 0.0 { + if dest.minify() && *x == 1.0 && *y == 0.0 && *z == 0.0 { // rotate3d(1, 0, 0, a) => rotateX(a) dest.write_str("rotateX(")?; angle.to_css_with_unitless_zero(dest)?; - } else if dest.options().minify && *x == 0.0 && *y == 1.0 && *z == 0.0 { + } else if dest.minify() && *x == 0.0 && *y == 1.0 && *z == 0.0 { // rotate3d(0, 1, 0, a) => rotateY(a) dest.write_str("rotateY(")?; angle.to_css_with_unitless_zero(dest)?; - } else if dest.options().minify && *x == 0.0 && *y == 0.0 && *z == 1.0 { + } else if dest.minify() && *x == 0.0 && *y == 0.0 && *z == 1.0 { // rotate3d(0, 0, 1, a) => rotate(a) dest.write_str("rotate(")?; angle.to_css_with_unitless_zero(dest)?; @@ -1211,7 +1207,7 @@ impl ToCss for Transform { Ok(()) } Skew(x, y) => { - if dest.options().minify && x.is_zero() && !y.is_zero() { + if dest.minify() && x.is_zero() && !y.is_zero() { dest.write_str("skewY(")?; y.to_css_with_unitless_zero(dest)? } else { @@ -1226,7 +1222,7 @@ impl ToCss for Transform { Ok(()) } SkewX(angle) => { - dest.write_str(if dest.options().minify { "skew(" } else { "skewX(" })?; + dest.write_str(if dest.minify() { "skew(" } else { "skewX(" })?; angle.to_css_with_unitless_zero(dest)?; dest.write_char(')')?; Ok(()) diff --git a/src/rules/font_face.rs b/src/rules/font_face.rs index 06b153f3b..151b29f45 100644 --- a/src/rules/font_face.rs +++ b/src/rules/font_face.rs @@ -510,7 +510,7 @@ impl<'i> ToCss for FontFaceRule<'i> { for (i, prop) in self.properties.iter().enumerate() { dest.newline()?; prop.to_css(dest)?; - if i != len - 1 || !dest.options().minify { + if i != len - 1 || !dest.minify() { dest.write_char(';')?; } } diff --git a/src/rules/font_feature_values.rs b/src/rules/font_feature_values.rs index 9a3c2a612..986bd7275 100644 --- a/src/rules/font_feature_values.rs +++ b/src/rules/font_feature_values.rs @@ -257,7 +257,7 @@ impl<'i> ToCss for FontFeatureSubrule<'i> { index.to_css(dest)?; } - if i != len - 1 || !dest.options().minify { + if i != len - 1 || !dest.minify() { dest.write_char(';')?; } } diff --git a/src/rules/font_palette_values.rs b/src/rules/font_palette_values.rs index b64964f64..bd3bd43f1 100644 --- a/src/rules/font_palette_values.rs +++ b/src/rules/font_palette_values.rs @@ -368,7 +368,7 @@ impl<'i> ToCss for FontPaletteValuesRule<'i> { for (i, prop) in self.properties.iter().enumerate() { dest.newline()?; prop.to_css(dest)?; - if i != len - 1 || !dest.options().minify { + if i != len - 1 || !dest.minify() { dest.write_char(';')?; } } diff --git a/src/rules/keyframes.rs b/src/rules/keyframes.rs index 9f1257fc8..6eebff542 100644 --- a/src/rules/keyframes.rs +++ b/src/rules/keyframes.rs @@ -224,7 +224,7 @@ impl<'i> ToCss for KeyframesRule<'i> { if first_rule { first_rule = false; } else { - if !dest.options().minify { + if !dest.minify() { dest.write_char('\n')?; // no indent } dest.newline()?; @@ -240,7 +240,7 @@ impl<'i> ToCss for KeyframesRule<'i> { for keyframe in &self.keyframes { if first { first = false; - } else if !dest.options().minify { + } else if !dest.minify() { dest.write_char('\n')?; // no indent } dest.newline()?; @@ -312,7 +312,7 @@ impl ToCss for KeyframeSelector { fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { KeyframeSelector::Percentage(p) => { - if dest.options().minify && *p == Percentage(1.0) { + if dest.minify() && *p == Percentage(1.0) { dest.write_str("to")?; Ok(()) } else { @@ -320,7 +320,7 @@ impl ToCss for KeyframeSelector { } } KeyframeSelector::From => { - if dest.options().minify { + if dest.minify() { dest.write_str("0%")?; Ok(()) } else { diff --git a/src/rules/media.rs b/src/rules/media.rs index bd0de95a1..f2ee491a9 100644 --- a/src/rules/media.rs +++ b/src/rules/media.rs @@ -46,7 +46,7 @@ impl<'i, T: Clone> MediaRule<'i, T> { impl<'a, 'i, T: ToCss> ToCss for MediaRule<'i, T> { fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { // If the media query always matches, we can just output the nested rules. - if dest.options().minify && self.query.always_matches() { + if dest.minify() && self.query.always_matches() { self.rules.to_css(dest)?; return Ok(()); } diff --git a/src/rules/mod.rs b/src/rules/mod.rs index 5920215e9..a79abbf9b 100644 --- a/src/rules/mod.rs +++ b/src/rules/mod.rs @@ -1144,7 +1144,7 @@ impl<'a, 'i, T: ToCss> ToCss for CssRuleList<'i, T> { if first { first = false; } else { - if !dest.options().minify + if !dest.minify() && !(last_without_block && matches!( rule, @@ -1158,7 +1158,7 @@ impl<'a, 'i, T: ToCss> ToCss for CssRuleList<'i, T> { rule.to_css(dest)?; // If this is an invisible nested declarations rule, and not the last rule in the block, add a semicolon. - if dest.options().minify + if dest.minify() && !should_compile!(dest.state().targets.current, Nesting) && matches!(rule, CssRule::NestedDeclarations(_)) && i != self.0.len() - 1 diff --git a/src/rules/page.rs b/src/rules/page.rs index e2b09a1cb..7560c8338 100644 --- a/src/rules/page.rs +++ b/src/rules/page.rs @@ -209,7 +209,7 @@ impl<'i> ToCss for PageRule<'i> { dest.write_str("@page")?; if let Some(first) = self.selectors.first() { // Space is only required if the first selector has a name. - if !dest.options().minify || first.name.is_some() { + if !dest.minify() || first.name.is_some() { dest.write_char(' ')?; } let mut first = true; @@ -235,7 +235,7 @@ impl<'i> ToCss for PageRule<'i> { for decl in &$decls { dest.newline()?; decl.to_css(dest, $important)?; - if i != len - 1 || !dest.options().minify { + if i != len - 1 || !dest.minify() { dest.write_char(';')?; } i += 1; @@ -247,7 +247,7 @@ impl<'i> ToCss for PageRule<'i> { write!(self.declarations.important_declarations, true); if !self.rules.is_empty() { - if !dest.options().minify && self.declarations.len() > 0 { + if !dest.minify() && self.declarations.len() > 0 { dest.write_char('\n')?; } dest.newline()?; @@ -257,7 +257,7 @@ impl<'i> ToCss for PageRule<'i> { if first { first = false; } else { - if !dest.options().minify { + if !dest.minify() { dest.write_char('\n')?; } dest.newline()?; diff --git a/src/rules/property.rs b/src/rules/property.rs index 772d88540..60b5b817d 100644 --- a/src/rules/property.rs +++ b/src/rules/property.rs @@ -139,7 +139,7 @@ impl<'i> ToCss for PropertyRule<'i> { dest.whitespace()?; initial_value.to_css(dest)?; - if !dest.options().minify { + if !dest.minify() { dest.write_char(';')?; } } diff --git a/src/rules/scope.rs b/src/rules/scope.rs index 41e7e60c9..b72955921 100644 --- a/src/rules/scope.rs +++ b/src/rules/scope.rs @@ -74,7 +74,7 @@ impl<'i, T: ToCss> ToCss for ScopeRule<'i, T> { dest.whitespace()?; } if let Some(scope_end) = &self.scope_end { - if dest.options().minify { + if dest.minify() { dest.write_char(' ')?; } dest.write_str("to (")?; diff --git a/src/rules/style.rs b/src/rules/style.rs index ef281fe58..06d46ceb5 100644 --- a/src/rules/style.rs +++ b/src/rules/style.rs @@ -219,7 +219,7 @@ impl<'a, 'i, T: ToCss> ToCss for StyleRule<'i, T> { if first_rule { first_rule = false; } else { - if !dest.options().minify { + if !dest.minify() { dest.write_char('\n')?; // no indent } dest.newline()?; @@ -265,10 +265,7 @@ impl<'a, 'i, T: ToCss> StyleRule<'i, T> { macro_rules! newline { () => { - if !dest.options().minify - && (supports_nesting || len > 0 || has_preserved_rules) - && !self.rules.0.is_empty() - { + if !dest.minify() && (supports_nesting || len > 0 || has_preserved_rules) && !self.rules.0.is_empty() { if len > 0 || has_preserved_rules { dest.write_char('\n')?; } @@ -295,7 +292,7 @@ impl<'a, 'i, T: ToCss> StyleRule<'i, T> { } else { let mut first = true; for rule in self.rules.0.iter().filter(|rule| must_preserve_rule(rule)) { - if !dest.options().minify || first { + if !dest.minify() || first { dest.newline()?; } rule.to_css(dest)?; @@ -318,7 +315,7 @@ impl<'a, 'i, T: ToCss> StyleRule<'i, T> { if first { first = false; } else { - if !dest.options().minify { + if !dest.minify() { dest.write_char('\n')?; } dest.newline()?; diff --git a/src/rules/view_transition.rs b/src/rules/view_transition.rs index 5578bb07d..ced31b75d 100644 --- a/src/rules/view_transition.rs +++ b/src/rules/view_transition.rs @@ -156,7 +156,7 @@ impl<'i> ToCss for ViewTransitionRule<'i> { for (i, prop) in self.properties.iter().enumerate() { dest.newline()?; prop.to_css(dest)?; - if i != len - 1 || !dest.options().minify { + if i != len - 1 || !dest.minify() { dest.write_char(';')?; } } diff --git a/src/selector.rs b/src/selector.rs index 956592ff6..a57687df5 100644 --- a/src/selector.rs +++ b/src/selector.rs @@ -728,9 +728,7 @@ fn serialize_pseudo_class<'a, 'i, PrinterT: crate::printer::PrinterTrait>( macro_rules! pseudo { ($key: ident, $s: literal) => {{ let class = dest - .options() - .pseudo_classes - .as_ref() + .pseudo_classes() .and_then(|pseudo_classes| pseudo_classes.$key) .map(|class| unsafe { std::mem::transmute::<&str, &'static str>(class) }); @@ -1789,7 +1787,7 @@ fn serialize_component<'a, 'i, PrinterT: crate::printer::PrinterTrait>( cssparser::ToCss::to_css(local_name, dest)?; cssparser::ToCss::to_css(operator, dest)?; - if dest.options().minify { + if dest.minify() { // Serialize as both an identifier and a string and choose the shorter one. let mut id = String::new(); serialize_identifier(&value, &mut id)?; diff --git a/src/stylesheet.rs b/src/stylesheet.rs index c21fd73d1..dcf03be15 100644 --- a/src/stylesheet.rs +++ b/src/stylesheet.rs @@ -9,9 +9,7 @@ use crate::declaration::{DeclarationBlock, DeclarationHandler}; use crate::dependencies::Dependency; use crate::error::{Error, ErrorLocation, MinifyErrorKind, ParserError, PrinterError, PrinterErrorKind}; use crate::parser::{DefaultAtRule, DefaultAtRuleParser, TopLevelRuleParser}; -#[cfg(feature = "custom_sourcemap")] -use crate::printer::SourceMap; -use crate::printer::{PrinterTrait, Printer}; +use crate::printer::{Printer, PrinterTrait, SourceMap}; use crate::rules::{CssRule, CssRuleList, MinifyContext}; use crate::targets::{should_compile, Targets, TargetsWithSupportsScope}; use crate::traits::{AtRuleParser, ToCss}; @@ -55,7 +53,7 @@ pub use crate::printer::PseudoClasses; /// stylesheet.minify(MinifyOptions::default()).unwrap(); /// /// // Serialize it to a string. -/// let res = stylesheet.to_css(PrinterOptions::default(), None::<&mut ()>).unwrap(); +/// let res = stylesheet.to_css(PrinterOptions::default()).unwrap(); /// assert_eq!(res.code, ".foo, .bar {\n color: red;\n}\n"); /// ``` #[derive(Debug, Clone)] @@ -264,34 +262,21 @@ where } /// Serialize the style sheet to a CSS string. - #[cfg(feature = "custom_sourcemap")] pub fn to_css<'a, S: SourceMap>( &self, - options: PrinterOptions<'a>, - source_map: Option<&'a mut S>, + options: PrinterOptions<'a, S>, ) -> Result> { - self.to_css_impl(options, source_map) - } - - /// Serialize the style sheet to a CSS string. - #[cfg(not(feature = "custom_sourcemap"))] - pub fn to_css<'a>(&self, options: PrinterOptions<'a>) -> Result> { self.to_css_impl(options) } - fn to_css_impl<'a, #[cfg(feature = "custom_sourcemap")] S: SourceMap>( + fn to_css_impl<'a, S: SourceMap>( &self, - options: PrinterOptions<'a>, - #[cfg(feature = "custom_sourcemap")] source_map: Option<&'a mut S>, + options: PrinterOptions<'a, S>, ) -> Result> { // Make sure we always have capacity > 0: https://github.com/napi-rs/napi-rs/issues/1124. let mut dest = String::with_capacity(1); let project_root = options.project_root.clone(); - let printer = Printer::new(&mut dest, options); - #[cfg(feature = "custom_sourcemap")] - let mut printer = printer.with_source_map(source_map); - #[cfg(not(feature = "custom_sourcemap"))] - let mut printer = printer; + let mut printer = Printer::new_with_options(&mut dest, options); printer.sources = Some(&self.sources); #[cfg(feature = "custom_sourcemap")] diff --git a/src/traits.rs b/src/traits.rs index da5781b3e..d5cc2fa93 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -3,7 +3,7 @@ use crate::context::PropertyHandlerContext; use crate::declaration::{DeclarationBlock, DeclarationList}; use crate::error::{ParserError, PrinterError}; -use crate::printer::{PrinterTrait, Printer}; +use crate::printer::{Printer, PrinterTrait}; use crate::properties::{Property, PropertyId}; use crate::stylesheet::{ParserOptions, PrinterOptions}; use crate::targets::{Browsers, Targets}; diff --git a/src/values/color.rs b/src/values/color.rs index 43bc91b6f..f44d77953 100644 --- a/src/values/color.rs +++ b/src/values/color.rs @@ -654,7 +654,7 @@ impl ToCss for CssColor { if should_compile!(dest.state().targets.current, HexAlphaColors) { // If the browser doesn't support `#rrggbbaa` color syntax, it is converted to `transparent` when compressed(minify = true). // https://www.w3.org/TR/css-color-4/#transparent-black - if dest.options().minify && color.red == 0 && color.green == 0 && color.blue == 0 && color.alpha == 0 { + if dest.minify() && color.red == 0 && color.green == 0 && color.blue == 0 && color.alpha == 0 { dest.write_str("transparent")?; return Ok(()); } else { diff --git a/src/values/gradient.rs b/src/values/gradient.rs index c440a8b98..66dcdf034 100644 --- a/src/values/gradient.rs +++ b/src/values/gradient.rs @@ -282,7 +282,7 @@ impl LinearGradient { // If we have `to top` or `0deg`, and all of the positions and hints are percentages, // we can flip the gradient the other direction and omit the direction. } else if angle == 0.0 - && dest.options().minify + && dest.minify() && self.items.iter().all(|item| { matches!( item, @@ -502,7 +502,7 @@ impl LineDirection { match self { LineDirection::Angle(angle) => angle.to_css(dest), LineDirection::Horizontal(k) => { - if dest.options().minify { + if dest.minify() { match k { HorizontalPositionKeyword::Left => { dest.write_str("270deg")?; @@ -521,7 +521,7 @@ impl LineDirection { } } LineDirection::Vertical(k) => { - if dest.options().minify { + if dest.minify() { match k { VerticalPositionKeyword::Top => { dest.write_str("0deg")?; @@ -1339,7 +1339,7 @@ impl> ToCss for WebKitGradientPointCom use WebKitGradientPointComponent::*; match &self { Center => { - if dest.options().minify { + if dest.minify() { dest.write_str("50%")?; Ok(()) } else { @@ -1356,7 +1356,7 @@ impl> ToCss for WebKitGradientPointCom } } Side(s) => { - if dest.options().minify { + if dest.minify() { let lp: LengthPercentage = s.clone().into(); lp.to_css(dest)?; } else { diff --git a/src/values/position.rs b/src/values/position.rs index 688cfaaf0..88517fd61 100644 --- a/src/values/position.rs +++ b/src/values/position.rs @@ -361,7 +361,7 @@ impl ToCss for PositionComponent { use PositionComponent::*; match &self { Center => { - if dest.options().minify { + if dest.minify() { dest.write_str("50%")?; Ok(()) } else { diff --git a/src/values/url.rs b/src/values/url.rs index ac88356db..792f259da 100644 --- a/src/values/url.rs +++ b/src/values/url.rs @@ -60,7 +60,7 @@ impl<'i> ToCss for Url<'i> { } use cssparser::ToCss; - if dest.options().minify { + if dest.minify() { let mut buf = String::new(); Token::UnquotedUrl(CowRcStr::from(self.url.as_ref())).to_css(&mut buf)?; diff --git a/src/visitor.rs b/src/visitor.rs index 969576cf1..b23b54119 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -55,7 +55,7 @@ //! stylesheet.visit(&mut MyVisitor).unwrap(); //! //! let res = stylesheet -//! .to_css(PrinterOptions { minify: true, ..Default::default() }, None::<&mut ()>) +//! .to_css(PrinterOptions { minify: true, ..Default::default() }) //! .unwrap(); //! assert_eq!(res.code, ".foo{background:url(https://mywebsite.com/bg.png);width:2rem}"); //! ``` From 775e095549ae64552e76a5925b8372639101bb43 Mon Sep 17 00:00:00 2001 From: intellild Date: Wed, 2 Sep 2026 15:44:53 +0800 Subject: [PATCH 4/9] Revert "Move source maps into printer options" This reverts commit ca480a6f6cdb5bf61c1fd03be272a8a0aa4a6835. --- src/bundler.rs | 28 +++-- src/declaration.rs | 2 +- src/lib.rs | 196 ++++++++++++++++++------------- src/main.rs | 4 +- src/printer.rs | 116 ++++-------------- src/properties/background.rs | 2 +- src/properties/custom.rs | 4 +- src/properties/font.rs | 6 +- src/properties/grid.rs | 16 +-- src/properties/transform.rs | 44 +++---- src/rules/font_face.rs | 2 +- src/rules/font_feature_values.rs | 2 +- src/rules/font_palette_values.rs | 2 +- src/rules/keyframes.rs | 8 +- src/rules/media.rs | 2 +- src/rules/mod.rs | 4 +- src/rules/page.rs | 8 +- src/rules/property.rs | 2 +- src/rules/scope.rs | 2 +- src/rules/style.rs | 11 +- src/rules/view_transition.rs | 2 +- src/selector.rs | 6 +- src/stylesheet.rs | 27 ++++- src/traits.rs | 2 +- src/values/color.rs | 2 +- src/values/gradient.rs | 10 +- src/values/position.rs | 2 +- src/values/url.rs | 2 +- src/visitor.rs | 2 +- 29 files changed, 258 insertions(+), 258 deletions(-) diff --git a/src/bundler.rs b/src/bundler.rs index dce46a50e..717bf6109 100644 --- a/src/bundler.rs +++ b/src/bundler.rs @@ -958,7 +958,7 @@ mod tests { fn bundle(fs: P, entry: &str) -> String { let mut bundler = Bundler::<_, _, ()>::new(&fs, None, ParserOptions::default()); let stylesheet = bundler.bundle(Path::new(entry)).unwrap(); - stylesheet.to_css(PrinterOptions::default()).unwrap().code + stylesheet.to_css(PrinterOptions::default(), None::<&mut ()>).unwrap().code } fn bundle_css_module( @@ -990,10 +990,13 @@ mod tests { let mut stylesheet = bundler.bundle(Path::new(entry)).unwrap(); stylesheet.minify(MinifyOptions::default()).unwrap(); let res = stylesheet - .to_css(PrinterOptions { - project_root, - ..PrinterOptions::default() - }) + .to_css( + PrinterOptions { + project_root, + ..PrinterOptions::default() + }, + None::<&mut ()>, + ) .unwrap(); (res.code, res.exports.unwrap()) } @@ -1022,10 +1025,13 @@ mod tests { }) .unwrap(); stylesheet - .to_css(PrinterOptions { - targets, - ..PrinterOptions::default() - }) + .to_css( + PrinterOptions { + targets, + ..PrinterOptions::default() + }, + None::<&mut ()>, + ) .unwrap() .code } @@ -2198,8 +2204,8 @@ mod tests { PrinterOptions { minify: true, ..PrinterOptions::default() - } - .with_source_map(Some(&mut sm)), + }, + Some(&mut sm), ) .unwrap(); let map = sm.to_json(None).unwrap(); diff --git a/src/declaration.rs b/src/declaration.rs index 7c158792b..36f30cacd 100644 --- a/src/declaration.rs +++ b/src/declaration.rs @@ -213,7 +213,7 @@ impl<'i> DeclarationBlock<'i> { } decl.to_css(dest, $important)?; - if i != len - 1 || !dest.minify() || has_nested_rules { + if i != len - 1 || !dest.options().minify || has_nested_rules { dest.write_char(';')?; } diff --git a/src/lib.rs b/src/lib.rs index 358f18068..262ccdda1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -138,7 +138,7 @@ mod tests { if let Err(e) = stylesheet.minify(MinifyOptions::default()) { panic_with_test_error("test_with_options", "minify", source, e); } - let res = match stylesheet.to_css(PrinterOptions::default()) { + let res = match stylesheet.to_css(PrinterOptions::default(), None::<&mut ()>) { Ok(res) => res, Err(e) => panic_with_test_error("test_with_options", "print", source, e), }; @@ -154,7 +154,7 @@ mod tests { if let Err(e) = stylesheet.minify(MinifyOptions::default()) { panic_with_test_error("test_with_printer_options", "minify", source, e); } - let res = match stylesheet.to_css(options) { + let res = match stylesheet.to_css(options, None::<&mut ()>) { Ok(res) => res, Err(e) => panic_with_test_error("test_with_printer_options", "print", source, e), }; @@ -189,11 +189,14 @@ mod tests { }) { panic_with_test_error("minify_test_with_options", "minify", source, e); } - let res = match stylesheet.to_css(PrinterOptions { - minify: true, - targets, - ..PrinterOptions::default() - }) { + let res = match stylesheet.to_css( + PrinterOptions { + minify: true, + targets, + ..PrinterOptions::default() + }, + None::<&mut ()>, + ) { Ok(res) => res, Err(e) => panic_with_test_error("minify_test_with_options", "print", source, e), }; @@ -227,10 +230,13 @@ mod tests { }) { panic_with_test_error("prefix_test", "minify", source, e); } - let res = match stylesheet.to_css(PrinterOptions { - targets: targets.into(), - ..PrinterOptions::default() - }) { + let res = match stylesheet.to_css( + PrinterOptions { + targets: targets.into(), + ..PrinterOptions::default() + }, + None::<&mut ()>, + ) { Ok(res) => res, Err(e) => panic_with_test_error("prefix_test", "print", source, e), }; @@ -283,10 +289,13 @@ mod tests { }) { panic_with_test_error("nesting_test_with_targets", "minify", source, e); } - let res = match stylesheet.to_css(PrinterOptions { - targets, - ..PrinterOptions::default() - }) { + let res = match stylesheet.to_css( + PrinterOptions { + targets, + ..PrinterOptions::default() + }, + None::<&mut ()>, + ) { Ok(res) => res, Err(e) => panic_with_test_error("nesting_test_with_targets", "print", source, e), }; @@ -302,7 +311,7 @@ mod tests { if let Err(e) = stylesheet.minify(MinifyOptions::default()) { panic_with_test_error("nesting_test_no_targets", "minify", source, e); } - let res = match stylesheet.to_css(PrinterOptions::default()) { + let res = match stylesheet.to_css(PrinterOptions::default(), None::<&mut ()>) { Ok(res) => res, Err(e) => panic_with_test_error("nesting_test_no_targets", "print", source, e), }; @@ -367,10 +376,13 @@ mod tests { if let Err(e) = stylesheet.minify(MinifyOptions::default()) { panic_with_test_error("css_modules_test", "minify", source, e); } - let res = match stylesheet.to_css(PrinterOptions { - minify, - ..Default::default() - }) { + let res = match stylesheet.to_css( + PrinterOptions { + minify, + ..Default::default() + }, + None::<&mut ()>, + ) { Ok(res) => res, Err(e) => panic_with_test_error("css_modules_test", "print", source, e), }; @@ -407,7 +419,7 @@ mod tests { }) { panic_with_test_error("custom_media_test", "minify", source, e); } - let res = match stylesheet.to_css(PrinterOptions::default()) { + let res = match stylesheet.to_css(PrinterOptions::default(), None::<&mut ()>) { Ok(res) => res, Err(e) => panic_with_test_error("custom_media_test", "print", source, e), }; @@ -27022,10 +27034,13 @@ mod tests { .unwrap(); stylesheet.minify(MinifyOptions::default()).unwrap(); let res = stylesheet - .to_css(PrinterOptions { - minify: true, - ..PrinterOptions::default() - }) + .to_css( + PrinterOptions { + minify: true, + ..PrinterOptions::default() + }, + None::<&mut ()>, + ) .unwrap(); assert_eq!(res.code, ".foo{color:#00f;& .bar{color:red}}"); @@ -27729,7 +27744,7 @@ mod tests { }, ) .unwrap(); - if let Err(err) = stylesheet.to_css(PrinterOptions::default()) { + if let Err(err) = stylesheet.to_css(PrinterOptions::default(), None::<&mut ()>) { assert_eq!(err.kind, PrinterErrorKind::InvalidCssModulesPatternInGrid); } else { unreachable!() @@ -28187,10 +28202,13 @@ mod tests { ) .unwrap(); let res = stylesheet - .to_css(PrinterOptions { - project_root: Some(project_root), - ..PrinterOptions::default() - }) + .to_css( + PrinterOptions { + project_root: Some(project_root), + ..PrinterOptions::default() + }, + None::<&mut ()>, + ) .unwrap(); assert_eq!( res.code, @@ -28229,14 +28247,17 @@ mod tests { .unwrap(); stylesheet.minify(MinifyOptions::default()).unwrap(); let res = stylesheet - .to_css(PrinterOptions { - targets: Browsers { - chrome: Some(95 << 16), - ..Browsers::default() - } - .into(), - ..Default::default() - }) + .to_css( + PrinterOptions { + targets: Browsers { + chrome: Some(95 << 16), + ..Browsers::default() + } + .into(), + ..Default::default() + }, + None::<&mut ()>, + ) .unwrap(); assert_eq!( res.code, @@ -28293,15 +28314,18 @@ mod tests { let stylesheet = StyleSheet::parse(&source, ParserOptions::default()).unwrap(); let res = stylesheet - .to_css(PrinterOptions { - pseudo_classes: Some(PseudoClasses { - hover: Some("is-hovered"), - active: Some("is-active"), - focus_visible: Some("focus-visible"), - ..PseudoClasses::default() - }), - ..PrinterOptions::default() - }) + .to_css( + PrinterOptions { + pseudo_classes: Some(PseudoClasses { + hover: Some("is-hovered"), + active: Some("is-active"), + focus_visible: Some("focus-visible"), + ..PseudoClasses::default() + }), + ..PrinterOptions::default() + }, + None::<&mut ()>, + ) .unwrap(); assert_eq!(res.code, expected); @@ -28327,13 +28351,16 @@ mod tests { ) .unwrap(); let res = stylesheet - .to_css(PrinterOptions { - pseudo_classes: Some(PseudoClasses { - hover: Some("is-hovered"), - ..PseudoClasses::default() - }), - ..PrinterOptions::default() - }) + .to_css( + PrinterOptions { + pseudo_classes: Some(PseudoClasses { + hover: Some("is-hovered"), + ..PseudoClasses::default() + }), + ..PrinterOptions::default() + }, + None::<&mut ()>, + ) .unwrap(); assert_eq!(res.code, expected); } @@ -28414,7 +28441,7 @@ mod tests { ..MinifyOptions::default() }) .unwrap(); - let res = stylesheet.to_css(PrinterOptions::default()).unwrap(); + let res = stylesheet.to_css(PrinterOptions::default(), None::<&mut ()>).unwrap(); assert_eq!(res.code, expected); let source = r#" @@ -28440,7 +28467,7 @@ mod tests { ..MinifyOptions::default() }) .unwrap(); - let res = stylesheet.to_css(PrinterOptions::default()).unwrap(); + let res = stylesheet.to_css(PrinterOptions::default(), None::<&mut ()>).unwrap(); assert_eq!(res.code, expected); let source = r#" @@ -28487,14 +28514,17 @@ mod tests { }) .unwrap(); let res = stylesheet - .to_css(PrinterOptions { - targets: Browsers { - chrome: Some(95 << 16), - ..Browsers::default() - } - .into(), - ..PrinterOptions::default() - }) + .to_css( + PrinterOptions { + targets: Browsers { + chrome: Some(95 << 16), + ..Browsers::default() + } + .into(), + ..PrinterOptions::default() + }, + None::<&mut ()>, + ) .unwrap(); assert_eq!(res.code, expected); @@ -28536,7 +28566,7 @@ mod tests { ..MinifyOptions::default() }) .unwrap(); - let res = stylesheet.to_css(PrinterOptions::default()).unwrap(); + let res = stylesheet.to_css(PrinterOptions::default(), None::<&mut ()>).unwrap(); assert_eq!(res.code, expected); } @@ -30380,11 +30410,14 @@ mod tests { .unwrap(); stylesheet.minify(MinifyOptions::default()).unwrap(); let res = stylesheet - .to_css(PrinterOptions { - analyze_dependencies: Some(Default::default()), - minify: true, - ..PrinterOptions::default() - }) + .to_css( + PrinterOptions { + analyze_dependencies: Some(Default::default()), + minify: true, + ..PrinterOptions::default() + }, + None::<&mut ()>, + ) .unwrap(); assert_eq!(res.code, expected); let dependencies = res.dependencies.unwrap(); @@ -30405,10 +30438,13 @@ mod tests { fn dep_error_test(source: &str, error: PrinterErrorKind) { let stylesheet = StyleSheet::parse(&source, ParserOptions::default()).unwrap(); - let res = stylesheet.to_css(PrinterOptions { - analyze_dependencies: Some(Default::default()), - ..PrinterOptions::default() - }); + let res = stylesheet.to_css( + PrinterOptions { + analyze_dependencies: Some(Default::default()), + ..PrinterOptions::default() + }, + None::<&mut ()>, + ); match res { Err(e) => assert_eq!(e.kind, error), _ => unreachable!(), @@ -31233,8 +31269,8 @@ mod tests { PrinterOptions { minify: true, ..PrinterOptions::default() - } - .with_source_map(Some(&mut source_map)), + }, + Some(&mut source_map), ) .unwrap(); @@ -31280,8 +31316,8 @@ mod tests { PrinterOptions { minify: true, ..PrinterOptions::default() - } - .with_source_map(Some(&mut sm)), + }, + Some(&mut sm), ) .unwrap(); let map = sm.to_json(None).unwrap(); @@ -31328,8 +31364,8 @@ mod tests { PrinterOptions { minify: true, ..PrinterOptions::default() - } - .with_source_map(Some(&mut sm)), + }, + Some(&mut sm), ) .unwrap(); let map = sm.to_json(None).unwrap(); diff --git a/src/main.rs b/src/main.rs index 1ae9feadb..27f9c8a51 100644 --- a/src/main.rs +++ b/src/main.rs @@ -219,8 +219,8 @@ pub fn main() -> Result<(), std::io::Error> { project_root: Some(&project_root.to_string_lossy()), targets, ..PrinterOptions::default() - } - .with_source_map(source_map.as_mut()), + }, + source_map.as_mut(), ) .unwrap() }; diff --git a/src/printer.rs b/src/printer.rs index da53f653e..685bcae80 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -205,12 +205,9 @@ impl SourceMap for ParcelSourceMap { } /// Options that control how CSS is serialized to a string. -pub struct PrinterOptions<'a, S: SourceMap = ()> { +pub struct PrinterOptions<'a> { /// Whether to minify the CSS, i.e. remove white space. pub minify: bool, - /// An optional reference to a source map to write mappings into. - #[cfg_attr(docsrs, doc(cfg(feature = "custom_sourcemap")))] - pub source_map: Option<&'a mut S>, /// An optional project root path, used to generate relative paths for sources used in CSS module hashes. pub project_root: Option<&'a str>, /// Targets to output the CSS for. @@ -232,7 +229,6 @@ impl<'a> Default for PrinterOptions<'a> { fn default() -> Self { PrinterOptions { minify: false, - source_map: None, project_root: None, targets: Targets::default(), analyze_dependencies: None, @@ -241,59 +237,6 @@ impl<'a> Default for PrinterOptions<'a> { } } -impl<'a, S: SourceMap> PrinterOptions<'a, S> { - /// Returns these options with a source map writer attached. - pub fn with_source_map(self, source_map: Option<&'a mut T>) -> PrinterOptions<'a, T> { - PrinterOptions { - minify: self.minify, - source_map, - project_root: self.project_root, - targets: self.targets, - analyze_dependencies: self.analyze_dependencies, - pseudo_classes: self.pseudo_classes, - } - } -} - -/// Serialization options exposed to CSS printers. -/// -/// Source map storage is intentionally excluded so printing code can depend on -/// configuration without depending on a concrete source map implementation. -pub trait PrinterOptionsTrait { - /// Whether to minify the CSS, i.e. remove white space. - fn minify(&self) -> bool; - /// An optional project root path, used to generate relative paths for sources used in CSS module hashes. - fn project_root(&self) -> Option<&str>; - /// Targets to output the CSS for. - fn targets(&self) -> Targets; - /// Whether to analyze dependencies (i.e. `@import` and `url()`). - fn analyze_dependencies(&self) -> Option<&DependencyOptions>; - /// A mapping of pseudo classes to replace with class names. - fn pseudo_classes(&self) -> Option<&PseudoClasses<'_>>; -} - -impl PrinterOptionsTrait for PrinterOptions<'_, S> { - fn minify(&self) -> bool { - self.minify - } - - fn project_root(&self) -> Option<&str> { - self.project_root - } - - fn targets(&self) -> Targets { - self.targets - } - - fn analyze_dependencies(&self) -> Option<&DependencyOptions> { - self.analyze_dependencies.as_ref() - } - - fn pseudo_classes(&self) -> Option<&PseudoClasses<'_>> { - self.pseudo_classes.as_ref() - } -} - #[allow(missing_docs)] pub struct PrinterState<'a, 'c> { pub(crate) loc: Location, @@ -338,7 +281,7 @@ pub struct PseudoClasses<'a> { pub struct Printer<'a, 'c, W, S: SourceMap = ()> { pub(crate) sources: Option<&'c Vec>, dest: &'a mut W, - pub(crate) options: PrinterOptions<'a, S>, + pub(crate) options: PrinterOptions<'a>, pub(crate) state: PrinterState<'a, 'c>, #[cfg(feature = "custom_sourcemap")] pub(crate) source_map: Option<&'a mut S>, @@ -356,30 +299,18 @@ impl<'a, 'c, W: std::fmt::Write + Sized> Printer<'a, 'c, W, ()> { } impl<'a, 'c, W: std::fmt::Write + Sized, S: SourceMap> Printer<'a, 'c, W, S> { - /// Create a new Printer wrapping the given destination and options. - pub fn new_with_options(dest: &'a mut W, options: PrinterOptions<'a, S>) -> Self { - Self::new_impl(dest, options) - } - - fn new_impl( - dest: &'a mut W, - #[cfg_attr(not(feature = "custom_sourcemap"), allow(unused_mut))] mut options: PrinterOptions<'a, S>, - ) -> Self { - let dependencies = if options.analyze_dependencies().is_some() { + fn new_impl(dest: &'a mut W, options: PrinterOptions<'a>) -> Self { + let dependencies = if options.analyze_dependencies.is_some() { Some(Vec::new()) } else { None }; - let remove_imports = matches!(options.analyze_dependencies(), Some(d) if d.remove_imports); - #[cfg(feature = "custom_sourcemap")] - let mut source_map = None; - #[cfg(feature = "custom_sourcemap")] - std::mem::swap(&mut source_map, &mut options.source_map); + let remove_imports = matches!(&options.analyze_dependencies, Some(d) if d.remove_imports); Printer { sources: None, dest, #[cfg(feature = "custom_sourcemap")] - source_map, + source_map: None, state: PrinterState { loc: Location { source_index: 0, @@ -389,7 +320,7 @@ impl<'a, 'c, W: std::fmt::Write + Sized, S: SourceMap> Printer<'a, 'c, W, S> { generated_line: 0, generated_col: 0, indent: 0, - targets: TargetsWithSupportsScope::new(options.targets()), + targets: TargetsWithSupportsScope::new(options.targets), vendor_prefix: VendorPrefix::empty(), in_calc: false, css_module: None, @@ -405,6 +336,19 @@ impl<'a, 'c, W: std::fmt::Write + Sized, S: SourceMap> Printer<'a, 'c, W, S> { } } + #[cfg(feature = "custom_sourcemap")] + /// Attaches a source map writer to this printer. + pub fn with_source_map(self, source_map: Option<&'a mut T>) -> Printer<'a, 'c, W, T> { + Printer { + sources: self.sources, + dest: self.dest, + options: self.options, + state: self.state, + source_map, + source_maps: Vec::new(), + } + } + /// Returns the current source filename that is being printed. pub fn filename(&self) -> &'c str { if let Some(sources) = self.sources { @@ -464,7 +408,7 @@ impl<'a, 'c, W: std::fmt::Write + Sized, S: SourceMap> Printer<'a, 'c, W, S> { /// Use `write_char` instead if you wish to force a space character to be written, /// regardless of the `minify` option. pub fn whitespace(&mut self) -> Result<(), PrinterError> { - if self.options.minify() { + if self.options.minify { return Ok(()); } @@ -484,7 +428,7 @@ impl<'a, 'c, W: std::fmt::Write + Sized, S: SourceMap> Printer<'a, 'c, W, S> { /// Writes a newline character followed by indentation. /// If the `minify` option is enabled, then nothing is printed. pub fn newline(&mut self) -> Result<(), PrinterError> { - if self.options.minify() { + if self.options.minify { return Ok(()); } @@ -662,17 +606,9 @@ mod private { /// A printer abstraction used by CSS serialization implementations. #[allow(missing_docs)] pub trait PrinterTrait: std::fmt::Write + private::Sealed { - type Options: PrinterOptionsTrait; - - fn options(&self) -> &Self::Options; + fn options(&self) -> &PrinterOptions<'_>; fn state(&self) -> &PrinterState<'_, '_>; fn state_mut(&mut self) -> &mut PrinterState<'_, '_>; - fn minify(&self) -> bool { - self.options().minify() - } - fn pseudo_classes(&self) -> Option<&PseudoClasses<'_>> { - self.options().pseudo_classes() - } fn filename(&self) -> &str; fn error(&self, kind: PrinterErrorKind, loc: crate::dependencies::Location) -> Error { Error { @@ -703,10 +639,8 @@ pub trait PrinterTrait: std::fmt::Write + private::Sealed { impl<'a, 'c, W: std::fmt::Write + Sized, S: SourceMap> private::Sealed for Printer<'a, 'c, W, S> {} impl<'a, 'c, W: std::fmt::Write + Sized, S: SourceMap> PrinterTrait for Printer<'a, 'c, W, S> { - type Options = PrinterOptions<'a, S>; - - fn options(&self) -> &Self::Options { - &self.options + fn options(&self) -> &PrinterOptions<'_> { + unsafe { std::mem::transmute::<&PrinterOptions<'a>, &PrinterOptions<'_>>(&self.options) } } fn state(&self) -> &PrinterState<'_, '_> { diff --git a/src/properties/background.rs b/src/properties/background.rs index c4a802742..b95a2f37a 100644 --- a/src/properties/background.rs +++ b/src/properties/background.rs @@ -515,7 +515,7 @@ impl<'i> ToCss for Background<'i> { // If nothing was output, then this is the initial value, e.g. background: transparent if !has_output { - if dest.minify() { + if dest.options().minify { // `0 0` is the shortest valid background value self.position.to_css(dest)?; } else { diff --git a/src/properties/custom.rs b/src/properties/custom.rs index ab09af17f..2687f29d2 100644 --- a/src/properties/custom.rs +++ b/src/properties/custom.rs @@ -885,14 +885,14 @@ impl<'a> ToCss for Token<'a> { } .to_css(dest)?, Token::WhiteSpace(w) => { - if dest.minify() { + if dest.options().minify { dest.write_char(' ')?; } else { dest.write_str(&w)?; } } Token::Comment(c) => { - if !dest.minify() { + if !dest.options().minify { cssparser::Token::Comment(c).to_css(dest)?; } } diff --git a/src/properties/font.rs b/src/properties/font.rs index fa0ec202c..b6a2b52c3 100644 --- a/src/properties/font.rs +++ b/src/properties/font.rs @@ -84,8 +84,8 @@ impl ToCss for AbsoluteFontWeight { use AbsoluteFontWeight::*; match self { Weight(val) => val.to_css(dest)?, - Normal => dest.write_str(if dest.minify() { "400" } else { "normal" })?, - Bold => dest.write_str(if dest.minify() { "700" } else { "bold" })?, + Normal => dest.write_str(if dest.options().minify { "400" } else { "normal" })?, + Bold => dest.write_str(if dest.options().minify { "700" } else { "bold" })?, }; Ok(()) } @@ -260,7 +260,7 @@ impl Into for &FontStretch { impl ToCss for FontStretch { fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { - if dest.minify() { + if dest.options().minify { let percentage: Percentage = self.into(); return percentage.to_css(dest); } diff --git a/src/properties/grid.rs b/src/properties/grid.rs index b76b45a4c..8020fbbba 100644 --- a/src/properties/grid.rs +++ b/src/properties/grid.rs @@ -641,7 +641,7 @@ impl ToCss for GridTemplateAreas { let mut next = iter.next(); let mut first = true; while next.is_some() { - if !first && !dest.minify() { + if !first && !dest.options().minify { dest.newline()?; } @@ -649,14 +649,14 @@ impl ToCss for GridTemplateAreas { if first { first = false; - if !dest.minify() { + if !dest.options().minify { // Indent by the width of "grid-template-areas: ", so the rows line up. dest.indent_by(21); } } } - if !dest.minify() { + if !dest.options().minify { dest.dedent_by(21); } @@ -684,13 +684,13 @@ impl GridTemplateAreas { for i in 0..columns { if let Some(token) = next { if let Some(string) = token { - if i > 0 && (!last_was_null || !dest.minify()) { + if i > 0 && (!last_was_null || !dest.options().minify) { dest.write_char(' ')?; } write_ident(string, dest)?; last_was_null = false; } else { - if i > 0 && (last_was_null || !dest.minify()) { + if i > 0 && (last_was_null || !dest.options().minify) { dest.write_char(' ')?; } dest.write_char('.')?; @@ -845,7 +845,7 @@ impl GridTemplate<'_> { while next.is_some() { macro_rules! newline { () => { - if !dest.minify() { + if !dest.options().minify { if !indented { // Indent by the width of "grid-template: ", so the rows line up. dest.indent_by(indent); @@ -858,7 +858,7 @@ impl GridTemplate<'_> { if let Some(line_names) = line_names_iter.next() { if !line_names.is_empty() { - if !dest.minify() && line_names.len() == 2 { + if !dest.options().minify && line_names.len() == 2 { dest.whitespace()?; serialize_line_names(&line_names[0..1], dest)?; newline!(); @@ -1088,7 +1088,7 @@ impl ToCss for GridAutoFlow { } else if *self == GridAutoFlow::Column { "column" } else if *self == GridAutoFlow::Row | GridAutoFlow::Dense { - if dest.minify() { + if dest.options().minify { "dense" } else { "row dense" diff --git a/src/properties/transform.rs b/src/properties/transform.rs index 750aa9f3f..2b1f67f78 100644 --- a/src/properties/transform.rs +++ b/src/properties/transform.rs @@ -55,7 +55,7 @@ impl ToCss for TransformList { // TODO: Re-enable with a better solution // See: https://github.com/parcel-bundler/lightningcss/issues/288 - // if dest.minify() { + // if dest.options().minify { // // Combine transforms into a single matrix. // if let Some(matrix) = self.to_matrix() { // // Generate based on the original transforms. @@ -1020,7 +1020,7 @@ impl ToCss for Transform { use Transform::*; match self { Translate(x, y) => { - if dest.minify() && x.is_zero() && !y.is_zero() { + if dest.options().minify && x.is_zero() && !y.is_zero() { dest.write_str("translateY(")?; y.to_css(dest)? } else { @@ -1035,7 +1035,11 @@ impl ToCss for Transform { Ok(()) } TranslateX(x) => { - dest.write_str(if dest.minify() { "translate(" } else { "translateX(" })?; + dest.write_str(if dest.options().minify { + "translate(" + } else { + "translateX(" + })?; x.to_css(dest)?; dest.write_char(')')?; Ok(()) @@ -1053,16 +1057,16 @@ impl ToCss for Transform { Ok(()) } Translate3d(x, y, z) => { - if dest.minify() && !x.is_zero() && y.is_zero() && z.is_zero() { + if dest.options().minify && !x.is_zero() && y.is_zero() && z.is_zero() { dest.write_str("translate(")?; x.to_css(dest)?; - } else if dest.minify() && x.is_zero() && !y.is_zero() && z.is_zero() { + } else if dest.options().minify && x.is_zero() && !y.is_zero() && z.is_zero() { dest.write_str("translateY(")?; y.to_css(dest)?; - } else if dest.minify() && x.is_zero() && y.is_zero() && !z.is_zero() { + } else if dest.options().minify && x.is_zero() && y.is_zero() && !z.is_zero() { dest.write_str("translateZ(")?; z.to_css(dest)?; - } else if dest.minify() && z.is_zero() { + } else if dest.options().minify && z.is_zero() { dest.write_str("translate(")?; x.to_css(dest)?; dest.delim(',', false)?; @@ -1081,10 +1085,10 @@ impl ToCss for Transform { Scale(x, y) => { let x: f32 = x.into(); let y: f32 = y.into(); - if dest.minify() && x == 1.0 && y != 1.0 { + if dest.options().minify && x == 1.0 && y != 1.0 { dest.write_str("scaleY(")?; y.to_css(dest)?; - } else if dest.minify() && x != 1.0 && y == 1.0 { + } else if dest.options().minify && x != 1.0 && y == 1.0 { dest.write_str("scaleX(")?; x.to_css(dest)?; } else { @@ -1123,23 +1127,23 @@ impl ToCss for Transform { let x: f32 = x.into(); let y: f32 = y.into(); let z: f32 = z.into(); - if dest.minify() && z == 1.0 && x == y { + if dest.options().minify && z == 1.0 && x == y { // scale3d(x, x, 1) => scale(x) dest.write_str("scale(")?; x.to_css(dest)?; - } else if dest.minify() && x != 1.0 && y == 1.0 && z == 1.0 { + } else if dest.options().minify && x != 1.0 && y == 1.0 && z == 1.0 { // scale3d(x, 1, 1) => scaleX(x) dest.write_str("scaleX(")?; x.to_css(dest)?; - } else if dest.minify() && x == 1.0 && y != 1.0 && z == 1.0 { + } else if dest.options().minify && x == 1.0 && y != 1.0 && z == 1.0 { // scale3d(1, y, 1) => scaleY(y) dest.write_str("scaleY(")?; y.to_css(dest)?; - } else if dest.minify() && x == 1.0 && y == 1.0 && z != 1.0 { + } else if dest.options().minify && x == 1.0 && y == 1.0 && z != 1.0 { // scale3d(1, 1, z) => scaleZ(z) dest.write_str("scaleZ(")?; z.to_css(dest)?; - } else if dest.minify() && z == 1.0 { + } else if dest.options().minify && z == 1.0 { // scale3d(x, y, 1) => scale(x, y) dest.write_str("scale(")?; x.to_css(dest)?; @@ -1175,21 +1179,21 @@ impl ToCss for Transform { Ok(()) } RotateZ(angle) => { - dest.write_str(if dest.minify() { "rotate(" } else { "rotateZ(" })?; + dest.write_str(if dest.options().minify { "rotate(" } else { "rotateZ(" })?; angle.to_css_with_unitless_zero(dest)?; dest.write_char(')')?; Ok(()) } Rotate3d(x, y, z, angle) => { - if dest.minify() && *x == 1.0 && *y == 0.0 && *z == 0.0 { + if dest.options().minify && *x == 1.0 && *y == 0.0 && *z == 0.0 { // rotate3d(1, 0, 0, a) => rotateX(a) dest.write_str("rotateX(")?; angle.to_css_with_unitless_zero(dest)?; - } else if dest.minify() && *x == 0.0 && *y == 1.0 && *z == 0.0 { + } else if dest.options().minify && *x == 0.0 && *y == 1.0 && *z == 0.0 { // rotate3d(0, 1, 0, a) => rotateY(a) dest.write_str("rotateY(")?; angle.to_css_with_unitless_zero(dest)?; - } else if dest.minify() && *x == 0.0 && *y == 0.0 && *z == 1.0 { + } else if dest.options().minify && *x == 0.0 && *y == 0.0 && *z == 1.0 { // rotate3d(0, 0, 1, a) => rotate(a) dest.write_str("rotate(")?; angle.to_css_with_unitless_zero(dest)?; @@ -1207,7 +1211,7 @@ impl ToCss for Transform { Ok(()) } Skew(x, y) => { - if dest.minify() && x.is_zero() && !y.is_zero() { + if dest.options().minify && x.is_zero() && !y.is_zero() { dest.write_str("skewY(")?; y.to_css_with_unitless_zero(dest)? } else { @@ -1222,7 +1226,7 @@ impl ToCss for Transform { Ok(()) } SkewX(angle) => { - dest.write_str(if dest.minify() { "skew(" } else { "skewX(" })?; + dest.write_str(if dest.options().minify { "skew(" } else { "skewX(" })?; angle.to_css_with_unitless_zero(dest)?; dest.write_char(')')?; Ok(()) diff --git a/src/rules/font_face.rs b/src/rules/font_face.rs index 151b29f45..06b153f3b 100644 --- a/src/rules/font_face.rs +++ b/src/rules/font_face.rs @@ -510,7 +510,7 @@ impl<'i> ToCss for FontFaceRule<'i> { for (i, prop) in self.properties.iter().enumerate() { dest.newline()?; prop.to_css(dest)?; - if i != len - 1 || !dest.minify() { + if i != len - 1 || !dest.options().minify { dest.write_char(';')?; } } diff --git a/src/rules/font_feature_values.rs b/src/rules/font_feature_values.rs index 986bd7275..9a3c2a612 100644 --- a/src/rules/font_feature_values.rs +++ b/src/rules/font_feature_values.rs @@ -257,7 +257,7 @@ impl<'i> ToCss for FontFeatureSubrule<'i> { index.to_css(dest)?; } - if i != len - 1 || !dest.minify() { + if i != len - 1 || !dest.options().minify { dest.write_char(';')?; } } diff --git a/src/rules/font_palette_values.rs b/src/rules/font_palette_values.rs index bd3bd43f1..b64964f64 100644 --- a/src/rules/font_palette_values.rs +++ b/src/rules/font_palette_values.rs @@ -368,7 +368,7 @@ impl<'i> ToCss for FontPaletteValuesRule<'i> { for (i, prop) in self.properties.iter().enumerate() { dest.newline()?; prop.to_css(dest)?; - if i != len - 1 || !dest.minify() { + if i != len - 1 || !dest.options().minify { dest.write_char(';')?; } } diff --git a/src/rules/keyframes.rs b/src/rules/keyframes.rs index 6eebff542..9f1257fc8 100644 --- a/src/rules/keyframes.rs +++ b/src/rules/keyframes.rs @@ -224,7 +224,7 @@ impl<'i> ToCss for KeyframesRule<'i> { if first_rule { first_rule = false; } else { - if !dest.minify() { + if !dest.options().minify { dest.write_char('\n')?; // no indent } dest.newline()?; @@ -240,7 +240,7 @@ impl<'i> ToCss for KeyframesRule<'i> { for keyframe in &self.keyframes { if first { first = false; - } else if !dest.minify() { + } else if !dest.options().minify { dest.write_char('\n')?; // no indent } dest.newline()?; @@ -312,7 +312,7 @@ impl ToCss for KeyframeSelector { fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { match self { KeyframeSelector::Percentage(p) => { - if dest.minify() && *p == Percentage(1.0) { + if dest.options().minify && *p == Percentage(1.0) { dest.write_str("to")?; Ok(()) } else { @@ -320,7 +320,7 @@ impl ToCss for KeyframeSelector { } } KeyframeSelector::From => { - if dest.minify() { + if dest.options().minify { dest.write_str("0%")?; Ok(()) } else { diff --git a/src/rules/media.rs b/src/rules/media.rs index f2ee491a9..bd0de95a1 100644 --- a/src/rules/media.rs +++ b/src/rules/media.rs @@ -46,7 +46,7 @@ impl<'i, T: Clone> MediaRule<'i, T> { impl<'a, 'i, T: ToCss> ToCss for MediaRule<'i, T> { fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { // If the media query always matches, we can just output the nested rules. - if dest.minify() && self.query.always_matches() { + if dest.options().minify && self.query.always_matches() { self.rules.to_css(dest)?; return Ok(()); } diff --git a/src/rules/mod.rs b/src/rules/mod.rs index a79abbf9b..5920215e9 100644 --- a/src/rules/mod.rs +++ b/src/rules/mod.rs @@ -1144,7 +1144,7 @@ impl<'a, 'i, T: ToCss> ToCss for CssRuleList<'i, T> { if first { first = false; } else { - if !dest.minify() + if !dest.options().minify && !(last_without_block && matches!( rule, @@ -1158,7 +1158,7 @@ impl<'a, 'i, T: ToCss> ToCss for CssRuleList<'i, T> { rule.to_css(dest)?; // If this is an invisible nested declarations rule, and not the last rule in the block, add a semicolon. - if dest.minify() + if dest.options().minify && !should_compile!(dest.state().targets.current, Nesting) && matches!(rule, CssRule::NestedDeclarations(_)) && i != self.0.len() - 1 diff --git a/src/rules/page.rs b/src/rules/page.rs index 7560c8338..e2b09a1cb 100644 --- a/src/rules/page.rs +++ b/src/rules/page.rs @@ -209,7 +209,7 @@ impl<'i> ToCss for PageRule<'i> { dest.write_str("@page")?; if let Some(first) = self.selectors.first() { // Space is only required if the first selector has a name. - if !dest.minify() || first.name.is_some() { + if !dest.options().minify || first.name.is_some() { dest.write_char(' ')?; } let mut first = true; @@ -235,7 +235,7 @@ impl<'i> ToCss for PageRule<'i> { for decl in &$decls { dest.newline()?; decl.to_css(dest, $important)?; - if i != len - 1 || !dest.minify() { + if i != len - 1 || !dest.options().minify { dest.write_char(';')?; } i += 1; @@ -247,7 +247,7 @@ impl<'i> ToCss for PageRule<'i> { write!(self.declarations.important_declarations, true); if !self.rules.is_empty() { - if !dest.minify() && self.declarations.len() > 0 { + if !dest.options().minify && self.declarations.len() > 0 { dest.write_char('\n')?; } dest.newline()?; @@ -257,7 +257,7 @@ impl<'i> ToCss for PageRule<'i> { if first { first = false; } else { - if !dest.minify() { + if !dest.options().minify { dest.write_char('\n')?; } dest.newline()?; diff --git a/src/rules/property.rs b/src/rules/property.rs index 60b5b817d..772d88540 100644 --- a/src/rules/property.rs +++ b/src/rules/property.rs @@ -139,7 +139,7 @@ impl<'i> ToCss for PropertyRule<'i> { dest.whitespace()?; initial_value.to_css(dest)?; - if !dest.minify() { + if !dest.options().minify { dest.write_char(';')?; } } diff --git a/src/rules/scope.rs b/src/rules/scope.rs index b72955921..41e7e60c9 100644 --- a/src/rules/scope.rs +++ b/src/rules/scope.rs @@ -74,7 +74,7 @@ impl<'i, T: ToCss> ToCss for ScopeRule<'i, T> { dest.whitespace()?; } if let Some(scope_end) = &self.scope_end { - if dest.minify() { + if dest.options().minify { dest.write_char(' ')?; } dest.write_str("to (")?; diff --git a/src/rules/style.rs b/src/rules/style.rs index 06d46ceb5..ef281fe58 100644 --- a/src/rules/style.rs +++ b/src/rules/style.rs @@ -219,7 +219,7 @@ impl<'a, 'i, T: ToCss> ToCss for StyleRule<'i, T> { if first_rule { first_rule = false; } else { - if !dest.minify() { + if !dest.options().minify { dest.write_char('\n')?; // no indent } dest.newline()?; @@ -265,7 +265,10 @@ impl<'a, 'i, T: ToCss> StyleRule<'i, T> { macro_rules! newline { () => { - if !dest.minify() && (supports_nesting || len > 0 || has_preserved_rules) && !self.rules.0.is_empty() { + if !dest.options().minify + && (supports_nesting || len > 0 || has_preserved_rules) + && !self.rules.0.is_empty() + { if len > 0 || has_preserved_rules { dest.write_char('\n')?; } @@ -292,7 +295,7 @@ impl<'a, 'i, T: ToCss> StyleRule<'i, T> { } else { let mut first = true; for rule in self.rules.0.iter().filter(|rule| must_preserve_rule(rule)) { - if !dest.minify() || first { + if !dest.options().minify || first { dest.newline()?; } rule.to_css(dest)?; @@ -315,7 +318,7 @@ impl<'a, 'i, T: ToCss> StyleRule<'i, T> { if first { first = false; } else { - if !dest.minify() { + if !dest.options().minify { dest.write_char('\n')?; } dest.newline()?; diff --git a/src/rules/view_transition.rs b/src/rules/view_transition.rs index ced31b75d..5578bb07d 100644 --- a/src/rules/view_transition.rs +++ b/src/rules/view_transition.rs @@ -156,7 +156,7 @@ impl<'i> ToCss for ViewTransitionRule<'i> { for (i, prop) in self.properties.iter().enumerate() { dest.newline()?; prop.to_css(dest)?; - if i != len - 1 || !dest.minify() { + if i != len - 1 || !dest.options().minify { dest.write_char(';')?; } } diff --git a/src/selector.rs b/src/selector.rs index a57687df5..956592ff6 100644 --- a/src/selector.rs +++ b/src/selector.rs @@ -728,7 +728,9 @@ fn serialize_pseudo_class<'a, 'i, PrinterT: crate::printer::PrinterTrait>( macro_rules! pseudo { ($key: ident, $s: literal) => {{ let class = dest - .pseudo_classes() + .options() + .pseudo_classes + .as_ref() .and_then(|pseudo_classes| pseudo_classes.$key) .map(|class| unsafe { std::mem::transmute::<&str, &'static str>(class) }); @@ -1787,7 +1789,7 @@ fn serialize_component<'a, 'i, PrinterT: crate::printer::PrinterTrait>( cssparser::ToCss::to_css(local_name, dest)?; cssparser::ToCss::to_css(operator, dest)?; - if dest.minify() { + if dest.options().minify { // Serialize as both an identifier and a string and choose the shorter one. let mut id = String::new(); serialize_identifier(&value, &mut id)?; diff --git a/src/stylesheet.rs b/src/stylesheet.rs index dcf03be15..c21fd73d1 100644 --- a/src/stylesheet.rs +++ b/src/stylesheet.rs @@ -9,7 +9,9 @@ use crate::declaration::{DeclarationBlock, DeclarationHandler}; use crate::dependencies::Dependency; use crate::error::{Error, ErrorLocation, MinifyErrorKind, ParserError, PrinterError, PrinterErrorKind}; use crate::parser::{DefaultAtRule, DefaultAtRuleParser, TopLevelRuleParser}; -use crate::printer::{Printer, PrinterTrait, SourceMap}; +#[cfg(feature = "custom_sourcemap")] +use crate::printer::SourceMap; +use crate::printer::{PrinterTrait, Printer}; use crate::rules::{CssRule, CssRuleList, MinifyContext}; use crate::targets::{should_compile, Targets, TargetsWithSupportsScope}; use crate::traits::{AtRuleParser, ToCss}; @@ -53,7 +55,7 @@ pub use crate::printer::PseudoClasses; /// stylesheet.minify(MinifyOptions::default()).unwrap(); /// /// // Serialize it to a string. -/// let res = stylesheet.to_css(PrinterOptions::default()).unwrap(); +/// let res = stylesheet.to_css(PrinterOptions::default(), None::<&mut ()>).unwrap(); /// assert_eq!(res.code, ".foo, .bar {\n color: red;\n}\n"); /// ``` #[derive(Debug, Clone)] @@ -262,21 +264,34 @@ where } /// Serialize the style sheet to a CSS string. + #[cfg(feature = "custom_sourcemap")] pub fn to_css<'a, S: SourceMap>( &self, - options: PrinterOptions<'a, S>, + options: PrinterOptions<'a>, + source_map: Option<&'a mut S>, ) -> Result> { + self.to_css_impl(options, source_map) + } + + /// Serialize the style sheet to a CSS string. + #[cfg(not(feature = "custom_sourcemap"))] + pub fn to_css<'a>(&self, options: PrinterOptions<'a>) -> Result> { self.to_css_impl(options) } - fn to_css_impl<'a, S: SourceMap>( + fn to_css_impl<'a, #[cfg(feature = "custom_sourcemap")] S: SourceMap>( &self, - options: PrinterOptions<'a, S>, + options: PrinterOptions<'a>, + #[cfg(feature = "custom_sourcemap")] source_map: Option<&'a mut S>, ) -> Result> { // Make sure we always have capacity > 0: https://github.com/napi-rs/napi-rs/issues/1124. let mut dest = String::with_capacity(1); let project_root = options.project_root.clone(); - let mut printer = Printer::new_with_options(&mut dest, options); + let printer = Printer::new(&mut dest, options); + #[cfg(feature = "custom_sourcemap")] + let mut printer = printer.with_source_map(source_map); + #[cfg(not(feature = "custom_sourcemap"))] + let mut printer = printer; printer.sources = Some(&self.sources); #[cfg(feature = "custom_sourcemap")] diff --git a/src/traits.rs b/src/traits.rs index d5cc2fa93..da5781b3e 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -3,7 +3,7 @@ use crate::context::PropertyHandlerContext; use crate::declaration::{DeclarationBlock, DeclarationList}; use crate::error::{ParserError, PrinterError}; -use crate::printer::{Printer, PrinterTrait}; +use crate::printer::{PrinterTrait, Printer}; use crate::properties::{Property, PropertyId}; use crate::stylesheet::{ParserOptions, PrinterOptions}; use crate::targets::{Browsers, Targets}; diff --git a/src/values/color.rs b/src/values/color.rs index f44d77953..43bc91b6f 100644 --- a/src/values/color.rs +++ b/src/values/color.rs @@ -654,7 +654,7 @@ impl ToCss for CssColor { if should_compile!(dest.state().targets.current, HexAlphaColors) { // If the browser doesn't support `#rrggbbaa` color syntax, it is converted to `transparent` when compressed(minify = true). // https://www.w3.org/TR/css-color-4/#transparent-black - if dest.minify() && color.red == 0 && color.green == 0 && color.blue == 0 && color.alpha == 0 { + if dest.options().minify && color.red == 0 && color.green == 0 && color.blue == 0 && color.alpha == 0 { dest.write_str("transparent")?; return Ok(()); } else { diff --git a/src/values/gradient.rs b/src/values/gradient.rs index 66dcdf034..c440a8b98 100644 --- a/src/values/gradient.rs +++ b/src/values/gradient.rs @@ -282,7 +282,7 @@ impl LinearGradient { // If we have `to top` or `0deg`, and all of the positions and hints are percentages, // we can flip the gradient the other direction and omit the direction. } else if angle == 0.0 - && dest.minify() + && dest.options().minify && self.items.iter().all(|item| { matches!( item, @@ -502,7 +502,7 @@ impl LineDirection { match self { LineDirection::Angle(angle) => angle.to_css(dest), LineDirection::Horizontal(k) => { - if dest.minify() { + if dest.options().minify { match k { HorizontalPositionKeyword::Left => { dest.write_str("270deg")?; @@ -521,7 +521,7 @@ impl LineDirection { } } LineDirection::Vertical(k) => { - if dest.minify() { + if dest.options().minify { match k { VerticalPositionKeyword::Top => { dest.write_str("0deg")?; @@ -1339,7 +1339,7 @@ impl> ToCss for WebKitGradientPointCom use WebKitGradientPointComponent::*; match &self { Center => { - if dest.minify() { + if dest.options().minify { dest.write_str("50%")?; Ok(()) } else { @@ -1356,7 +1356,7 @@ impl> ToCss for WebKitGradientPointCom } } Side(s) => { - if dest.minify() { + if dest.options().minify { let lp: LengthPercentage = s.clone().into(); lp.to_css(dest)?; } else { diff --git a/src/values/position.rs b/src/values/position.rs index 88517fd61..688cfaaf0 100644 --- a/src/values/position.rs +++ b/src/values/position.rs @@ -361,7 +361,7 @@ impl ToCss for PositionComponent { use PositionComponent::*; match &self { Center => { - if dest.minify() { + if dest.options().minify { dest.write_str("50%")?; Ok(()) } else { diff --git a/src/values/url.rs b/src/values/url.rs index 792f259da..ac88356db 100644 --- a/src/values/url.rs +++ b/src/values/url.rs @@ -60,7 +60,7 @@ impl<'i> ToCss for Url<'i> { } use cssparser::ToCss; - if dest.minify() { + if dest.options().minify { let mut buf = String::new(); Token::UnquotedUrl(CowRcStr::from(self.url.as_ref())).to_css(&mut buf)?; diff --git a/src/visitor.rs b/src/visitor.rs index b23b54119..969576cf1 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -55,7 +55,7 @@ //! stylesheet.visit(&mut MyVisitor).unwrap(); //! //! let res = stylesheet -//! .to_css(PrinterOptions { minify: true, ..Default::default() }) +//! .to_css(PrinterOptions { minify: true, ..Default::default() }, None::<&mut ()>) //! .unwrap(); //! assert_eq!(res.code, ".foo{background:url(https://mywebsite.com/bg.png);width:2rem}"); //! ``` From ef119dee905d6492d580ca58066a492dfa063595 Mon Sep 17 00:00:00 2001 From: AsyncIter Date: Tue, 8 Sep 2026 14:54:39 +0800 Subject: [PATCH 5/9] fix: address printer and source map review feedback --- Cargo.lock | 1 + Cargo.toml | 3 +- src/lib.rs | 73 +++++++++++++++++++--- src/printer.rs | 15 ++++- src/rules/style.rs | 151 ++++++++++++++++++++++++++++++++++++++++----- 5 files changed, 216 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 969046e61..5cccf3ebf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1595,6 +1595,7 @@ dependencies = [ "predicates 2.1.5", "pretty_assertions", "rayon", + "rustc-hash", "schemars", "serde", "serde-content", diff --git a/Cargo.toml b/Cargo.toml index a94ca6ba9..f3b1ad164 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,7 +50,7 @@ serde = [ "into_owned", ] sourcemap = ["custom_sourcemap", "dep:parcel_sourcemap"] -custom_sourcemap = [] +custom_sourcemap = ["dep:rustc-hash"] visitor = [] into_owned = [ "static-self", @@ -75,6 +75,7 @@ lazy_static = "1.4.0" const-str = "1.1.0" pathdiff = "0.2.1" ahash = "0.8.7" +rustc-hash = { version = "2", optional = true } pastey = "0.1.0" indexmap = { version = "2.2.6", features = ["serde"] } # CLI deps diff --git a/src/lib.rs b/src/lib.rs index 262ccdda1..86ae21fea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31241,20 +31241,77 @@ mod tests { None } - fn find_closest_mapping(&mut self, _line: u32, _column: u32) -> Option { - None + fn find_closest_mapping(&mut self, line: u32, column: u32) -> Option { + self + .mappings + .iter() + .rev() + .find(|(generated_line, generated_column, _)| (*generated_line, *generated_column) <= (line, column)) + .and_then(|(_, _, original)| *original) } - fn get_source(&self, _source_index: u32) -> Option<&str> { - None + fn get_source(&self, source_index: u32) -> Option<&str> { + self.sources.get(source_index as usize).map(String::as_str) } - fn get_name(&self, _name_index: u32) -> Option<&str> { - None + fn get_name(&self, name_index: u32) -> Option<&str> { + self.names.get(name_index as usize).map(String::as_str) } - fn get_source_content(&self, _source_index: u32) -> Option<&str> { - None + fn get_source_content(&self, source_index: u32) -> Option<&str> { + self + .sources_content + .iter() + .find(|(index, _)| *index == source_index) + .map(|(_, content)| content.as_str()) + } + } + + #[test] + fn test_custom_source_map_remapped_content() { + use crate::printer::Printer; + + for content in [None, Some(""), Some(".original { color: red }")] { + let original = OriginalLocation { + original_line: 10, + original_column: 5, + source: 0, + name: Some(0), + }; + let input_map = || TestSourceMap { + sources: vec!["original.scss".into()], + names: vec!["original".into()], + sources_content: content.map(|content| vec![(0, content.into())]).unwrap_or_default(), + mappings: vec![(0, 0, Some(original))], + }; + let mut output = TestSourceMap::default(); + // Ensure the cache uses output indices, which may differ from input indices. + output.add_source("existing.css"); + let mut css = String::new(); + let mut printer = Printer::new(&mut css, PrinterOptions::default()).with_source_map(Some(&mut output)); + printer.source_maps = vec![Some(input_map()), Some(input_map())]; + for source_index in [0, 1] { + for line in 0..100 { + printer.add_mapping(Location { + source_index, + line, + column: 1, + }); + } + } + drop(printer); + + assert_eq!(output.sources, vec!["existing.css", "original.scss"]); + assert_eq!(output.names, vec!["original"]); + assert_eq!( + output.sources_content, + content.map(|content| vec![(1, content.into())]).unwrap_or_default() + ); + assert_eq!(output.mappings.len(), 200); + assert!(output + .mappings + .iter() + .all(|(_, _, location)| *location == Some(OriginalLocation { source: 1, ..original }))); } } diff --git a/src/printer.rs b/src/printer.rs index 685bcae80..b7c68d421 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -9,6 +9,8 @@ use crate::vendor_prefix::VendorPrefix; use cssparser::{serialize_identifier, serialize_name}; #[cfg(feature = "sourcemap")] use parcel_sourcemap::SourceMap as ParcelSourceMap; +#[cfg(feature = "custom_sourcemap")] +use rustc_hash::FxHashSet; #[cfg(not(feature = "custom_sourcemap"))] use std::marker::PhantomData; @@ -287,6 +289,8 @@ pub struct Printer<'a, 'c, W, S: SourceMap = ()> { pub(crate) source_map: Option<&'a mut S>, #[cfg(feature = "custom_sourcemap")] pub(crate) source_maps: Vec>, + #[cfg(feature = "custom_sourcemap")] + sources_with_content: FxHashSet, #[cfg(not(feature = "custom_sourcemap"))] source_map: PhantomData S>, } @@ -331,6 +335,8 @@ impl<'a, 'c, W: std::fmt::Write + Sized, S: SourceMap> Printer<'a, 'c, W, S> { options, #[cfg(feature = "custom_sourcemap")] source_maps: Vec::new(), + #[cfg(feature = "custom_sourcemap")] + sources_with_content: FxHashSet::default(), #[cfg(not(feature = "custom_sourcemap"))] source_map: PhantomData, } @@ -346,6 +352,7 @@ impl<'a, 'c, W: std::fmt::Write + Sized, S: SourceMap> Printer<'a, 'c, W, S> { state: self.state, source_map, source_maps: Vec::new(), + sources_with_content: FxHashSet::default(), } } @@ -576,8 +583,12 @@ impl<'a, 'c, W: std::fmt::Write + Sized, S: SourceMap> Printer<'a, 'c, W, S> { original.source = source_index; original.name = name; - let content = sm.get_source_content(orig.source).unwrap().to_owned(); - map.set_source_content(source_index, &content); + if !self.sources_with_content.contains(&source_index) { + if let Some(content) = sm.get_source_content(orig.source) { + map.set_source_content(source_index, content); + self.sources_with_content.insert(source_index); + } + } found_mapping = true; } diff --git a/src/rules/style.rs b/src/rules/style.rs index ef281fe58..2a4919ee2 100644 --- a/src/rules/style.rs +++ b/src/rules/style.rs @@ -312,22 +312,7 @@ impl<'a, 'i, T: ToCss> StyleRule<'i, T> { parent: detach_style_context(parent), }; dest.state_mut().context = Some(style_context_ptr(&ctx)); - let res = { - let mut first = true; - for rule in self.rules.0.iter().filter(|rule| !must_preserve_rule(rule)) { - if first { - first = false; - } else { - if !dest.options().minify { - dest.write_char('\n')?; - } - dest.newline()?; - } - rule.to_css(dest)?; - } - - Ok::<(), PrinterError>(()) - }; + let res = self.to_css_hoisted_rules(dest); dest.state_mut().context = parent; res?; } @@ -335,4 +320,138 @@ impl<'a, 'i, T: ToCss> StyleRule<'i, T> { Ok(()) } + + fn to_css_hoisted_rules( + &self, + dest: &mut PrinterT, + ) -> Result<(), PrinterError> { + let mut first = true; + for rule in self.rules.0.iter().filter(|rule| !matches!(rule, CssRule::Unknown(_))) { + if first { + first = false; + } else { + if !dest.options().minify { + dest.write_char('\n')?; + } + dest.newline()?; + } + rule.to_css(dest)?; + } + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::printer::{Printer, PrinterOptions, PrinterTrait}; + use crate::stylesheet::{ParserOptions, StyleSheet}; + use crate::targets::Features; + use std::cell::Cell; + + fn nesting_options() -> PrinterOptions<'static> { + PrinterOptions { + targets: Targets { + include: Features::Nesting, + ..Targets::default() + }, + ..PrinterOptions::default() + } + } + + #[test] + fn restores_context_after_nested_writer_error() { + struct FailingWriter<'a> { + writes: &'a Cell, + fail_at: usize, + } + + impl std::fmt::Write for FailingWriter<'_> { + fn write_str(&mut self, _: &str) -> std::fmt::Result { + let index = self.writes.get(); + self.writes.set(index + 1); + if index == self.fail_at { + Err(std::fmt::Error) + } else { + Ok(()) + } + } + } + + let sheet = StyleSheet::parse( + ".a { & .b { & .c { color: red } } & .d { color: blue } }", + ParserOptions::default(), + ) + .unwrap(); + let CssRule::Style(rule) = &sheet.rules.0[0] else { + panic!("expected style rule"); + }; + let parent = StyleContext { + selectors: &rule.selectors, + parent: None, + }; + + for context in [None, Some(style_context_ptr(&parent))] { + let writes = Cell::new(0); + let mut writer = FailingWriter { + writes: &writes, + fail_at: usize::MAX, + }; + let mut printer = Printer::new(&mut writer, nesting_options()); + printer.state.context = context; + rule.to_css(&mut printer).unwrap(); + let total_writes = writes.get(); + + // Fail at every write, including separators between hoisted rules. + for fail_at in 0..total_writes { + writes.set(0); + let mut writer = FailingWriter { + writes: &writes, + fail_at, + }; + let mut printer = Printer::new(&mut writer, nesting_options()); + printer.state.context = context; + assert!(rule.to_css(&mut printer).is_err()); + assert_eq!(printer.state.context, context, "write {fail_at}"); + rule.to_css(&mut printer).unwrap(); + assert_eq!(printer.state.context, context); + } + } + } + + #[test] + fn restores_context_after_nested_custom_rule_error() { + struct FailingRule; + + impl ToCss for FailingRule { + fn to_css(&self, _: &mut P) -> Result<(), PrinterError> { + Err(std::fmt::Error.into()) + } + } + + let sheet = StyleSheet::parse(".a {}", ParserOptions::default()).unwrap(); + let CssRule::Style(parsed) = &sheet.rules.0[0] else { + panic!("expected style rule"); + }; + let rule = StyleRule { + selectors: parsed.selectors.clone(), + vendor_prefix: parsed.vendor_prefix, + declarations: parsed.declarations.clone(), + rules: CssRuleList(vec![CssRule::Custom(FailingRule)]), + loc: parsed.loc, + }; + let parent = StyleContext { + selectors: &parsed.selectors, + parent: None, + }; + for context in [None, Some(style_context_ptr(&parent))] { + let mut css = String::new(); + let mut printer = Printer::new(&mut css, nesting_options()); + printer.state.context = context; + assert!(rule.to_css(&mut printer).is_err()); + assert_eq!(printer.state.context, context); + parsed.to_css(&mut printer).unwrap(); + } + } } From 289ac16179f6cae31fd9d8d853dc043180347862 Mon Sep 17 00:00:00 2001 From: AsyncIter Date: Tue, 8 Sep 2026 21:49:33 +0800 Subject: [PATCH 6/9] fix: preserve custom source map indices when bundling --- Cargo.toml | 4 +- src/bundler.rs | 15 +++ src/printer.rs | 12 +- src/stylesheet.rs | 11 +- tests/test_bundler_source_map.rs | 187 +++++++++++++++++++++++++++++++ 5 files changed, 225 insertions(+), 4 deletions(-) create mode 100644 tests/test_bundler_source_map.rs diff --git a/Cargo.toml b/Cargo.toml index f3b1ad164..b638a3377 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,8 +36,8 @@ crate-type = ["rlib"] [features] default = ["bundler", "nodejs", "sourcemap"] browserslist = ["browserslist-rs"] -bundler = ["dashmap", "sourcemap", "rayon"] -cli = ["clap", "serde_json", "browserslist", "jemallocator"] +bundler = ["dashmap", "custom_sourcemap", "rayon"] +cli = ["clap", "serde_json", "browserslist", "jemallocator", "sourcemap"] jsonschema = ["schemars", "serde", "parcel_selectors/jsonschema"] nodejs = ["dep:serde", "dep:serde-content"] serde = [ diff --git a/src/bundler.rs b/src/bundler.rs index 717bf6109..124808e4a 100644 --- a/src/bundler.rs +++ b/src/bundler.rs @@ -79,6 +79,7 @@ enum AtRuleParserValue<'a, T> { struct BundleStyleSheet<'i, T> { stylesheet: Option>, + source_map_source_index: Option, dependencies: Vec, css_modules_deps: Vec, parent_source_index: u32, @@ -317,6 +318,16 @@ where let mut stylesheet = StyleSheet::new(sources, CssRuleList(rules), self.options.clone()); + if self.source_map.is_some() { + stylesheet.source_map_source_indices = self + .stylesheets + .get_mut() + .unwrap() + .iter() + .map(|s| s.source_map_source_index) + .collect(); + } + stylesheet.source_map_urls = self .stylesheets .get_mut() @@ -416,6 +427,7 @@ where stylesheets.push(BundleStyleSheet { stylesheet: None, + source_map_source_index: None, layer: rule.layer.clone(), media: rule.media.clone(), supports: rule.supports.clone(), @@ -456,6 +468,7 @@ where StyleSheet::::parse_with(code, opts, at_rule_parser)? }; + let mut source_map_source_index = None; if let Some(source_map) = &self.source_map { // Only add source if we don't have an input source map. // If we do, this will be handled by the printer when remapping locations. @@ -464,6 +477,7 @@ where let mut source_map = source_map.lock().unwrap(); let source_index = source_map.add_source(filename); source_map.set_source_content(source_index, code); + source_map_source_index = Some(source_index); } } @@ -607,6 +621,7 @@ where let entry = &mut self.stylesheets.lock().unwrap()[source_index as usize]; entry.stylesheet = Some(stylesheet); + entry.source_map_source_index = source_map_source_index; entry.dependencies = dependencies?; entry.css_modules_deps = css_modules_deps?; diff --git a/src/printer.rs b/src/printer.rs index b7c68d421..60b04d945 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -290,6 +290,8 @@ pub struct Printer<'a, 'c, W, S: SourceMap = ()> { #[cfg(feature = "custom_sourcemap")] pub(crate) source_maps: Vec>, #[cfg(feature = "custom_sourcemap")] + pub(crate) source_map_source_indices: &'c [Option], + #[cfg(feature = "custom_sourcemap")] sources_with_content: FxHashSet, #[cfg(not(feature = "custom_sourcemap"))] source_map: PhantomData S>, @@ -336,6 +338,8 @@ impl<'a, 'c, W: std::fmt::Write + Sized, S: SourceMap> Printer<'a, 'c, W, S> { #[cfg(feature = "custom_sourcemap")] source_maps: Vec::new(), #[cfg(feature = "custom_sourcemap")] + source_map_source_indices: &[], + #[cfg(feature = "custom_sourcemap")] sources_with_content: FxHashSet::default(), #[cfg(not(feature = "custom_sourcemap"))] source_map: PhantomData, @@ -352,6 +356,7 @@ impl<'a, 'c, W: std::fmt::Write + Sized, S: SourceMap> Printer<'a, 'c, W, S> { state: self.state, source_map, source_maps: Vec::new(), + source_map_source_indices: &[], sources_with_content: FxHashSet::default(), } } @@ -568,7 +573,12 @@ impl<'a, 'c, W: std::fmt::Write + Sized, S: SourceMap> Printer<'a, 'c, W, S> { let mut original = OriginalLocation { original_line: loc.line, original_column: loc.column - 1, - source: loc.source_index, + source: self + .source_map_source_indices + .get(loc.source_index as usize) + .copied() + .flatten() + .unwrap_or(loc.source_index), name: None, }; diff --git a/src/stylesheet.rs b/src/stylesheet.rs index c21fd73d1..032134338 100644 --- a/src/stylesheet.rs +++ b/src/stylesheet.rs @@ -11,7 +11,7 @@ use crate::error::{Error, ErrorLocation, MinifyErrorKind, ParserError, PrinterEr use crate::parser::{DefaultAtRule, DefaultAtRuleParser, TopLevelRuleParser}; #[cfg(feature = "custom_sourcemap")] use crate::printer::SourceMap; -use crate::printer::{PrinterTrait, Printer}; +use crate::printer::{Printer, PrinterTrait}; use crate::rules::{CssRule, CssRuleList, MinifyContext}; use crate::targets::{should_compile, Targets, TargetsWithSupportsScope}; use crate::traits::{AtRuleParser, ToCss}; @@ -79,6 +79,10 @@ pub struct StyleSheet<'i, T = DefaultAtRule> { pub sources: Vec, /// The source map URL extracted from the original style sheet. pub source_map_urls: Vec>, + /// Output source map indices corresponding to the bundler's source indices. + #[cfg(feature = "custom_sourcemap")] + #[cfg_attr(feature = "serde", serde(skip))] + pub(crate) source_map_source_indices: Vec>, /// The license comments that appeared at the start of the file. pub license_comments: Vec>, /// A list of content hashes for all source files included within the style sheet. @@ -134,6 +138,8 @@ where StyleSheet { sources, source_map_urls: Vec::new(), + #[cfg(feature = "custom_sourcemap")] + source_map_source_indices: Vec::new(), license_comments: Vec::new(), content_hashes: None, rules, @@ -201,6 +207,8 @@ where Ok(StyleSheet { sources: vec![options.filename.clone()], source_map_urls: vec![parser.current_source_map_url().map(|s| s.to_owned())], + #[cfg(feature = "custom_sourcemap")] + source_map_source_indices: Vec::new(), content_hashes, rules, license_comments, @@ -297,6 +305,7 @@ where #[cfg(feature = "custom_sourcemap")] if printer.source_map.is_some() { printer.source_maps = self.sources.iter().enumerate().map(|(i, _)| self.source_map::(i)).collect(); + printer.source_map_source_indices = &self.source_map_source_indices; } let (dependencies, exports, references) = self.print(&mut printer, project_root)?; diff --git a/tests/test_bundler_source_map.rs b/tests/test_bundler_source_map.rs new file mode 100644 index 000000000..d7c395c8b --- /dev/null +++ b/tests/test_bundler_source_map.rs @@ -0,0 +1,187 @@ +#![cfg(feature = "bundler")] + +use lightningcss::{ + bundler::{Bundler, ResolveResult, SourceProvider}, + printer::{OriginalLocation, SourceMap}, + rules::CssRule, + stylesheet::{ParserOptions, PrinterOptions}, +}; +use std::{ + collections::HashMap, + path::{Path, PathBuf}, +}; + +struct TestProvider(HashMap); + +impl SourceProvider for TestProvider { + type Error = std::io::Error; + + fn read<'a>(&'a self, file: &Path) -> Result<&'a str, Self::Error> { + Ok(self.0.get(file).unwrap()) + } + + fn resolve(&self, specifier: &str, originating_file: &Path) -> Result { + Ok(originating_file.with_file_name(specifier).into()) + } +} + +// This test writer supports one inline map fixture. +const INPUT_MAP: &str = r#"data:application/json,{"version":3,"sources":["/original.scss"],"sourcesContent":[".original{color:red}"],"names":[],"mappings":"AAYG"}"#; + +#[derive(Default)] +struct TestSourceMap { + sources: Vec<(u32, String, Option)>, + mappings: Vec, + input_mapping: Option, +} + +impl SourceMap for TestSourceMap { + fn add_source(&mut self, source: &str) -> u32 { + // Simulate a writer that canonicalizes and deduplicates source paths. + let source = if source == "/alias.css" { + "/dependency.css" + } else { + source + }; + if let Some((index, _, _)) = self.sources.iter().find(|(_, path, _)| path == source) { + return *index; + } + + // Source indices need not be contiguous or start at zero. + let index = 100 + self.sources.len() as u32 * 10; + self.sources.push((index, source.into(), None)); + index + } + + fn add_name(&mut self, _: &str) -> u32 { + unreachable!() + } + + fn set_source_content(&mut self, source_index: u32, source_content: &str) { + self.sources.iter_mut().find(|(index, _, _)| *index == source_index).unwrap().2 = Some(source_content.into()); + } + + fn add_mapping(&mut self, _: u32, _: u32, original: Option) { + self.mappings.push(original.unwrap()); + } + + fn from_data_url(_: &str, data_url: &str) -> Option { + if data_url != INPUT_MAP { + return None; + } + + let mut map = Self::default(); + let source = map.add_source("/original.scss"); + map.set_source_content(source, ".original{color:red}"); + map.input_mapping = Some(OriginalLocation { + original_line: 12, + original_column: 3, + source, + name: None, + }); + Some(map) + } + + fn find_closest_mapping(&mut self, _: u32, _: u32) -> Option { + self.input_mapping + } + + fn get_source(&self, source_index: u32) -> Option<&str> { + self + .sources + .iter() + .find(|(index, _, _)| *index == source_index) + .map(|(_, source, _)| source.as_str()) + } + + fn get_name(&self, _: u32) -> Option<&str> { + None + } + + fn get_source_content(&self, source_index: u32) -> Option<&str> { + self + .sources + .iter() + .find(|(index, _, _)| *index == source_index) + .and_then(|(_, _, content)| content.as_deref()) + } +} + +#[test] +fn translates_bundler_source_indices() { + let entry = "@import 'dependency.css';\n@import 'alias.css';\n.entry { color: red }"; + let dependency = ".dependency { color: blue }"; + let fs = TestProvider(HashMap::from([ + (PathBuf::from("/entry.css"), entry.into()), + (PathBuf::from("/dependency.css"), dependency.into()), + (PathBuf::from("/alias.css"), dependency.into()), + ])); + + for prepopulated in [false, true] { + let mut map = TestSourceMap::default(); + if prepopulated { + let existing = map.add_source("/existing.css"); + map.set_source_content(existing, "existing content"); + map.add_source("/dependency.css"); + } + let mut bundler = Bundler::new(&fs, Some(&mut map), ParserOptions::default()); + let stylesheet = bundler.bundle(Path::new("/entry.css")).unwrap(); + + // Rule locations must still refer to the stylesheet's internal source list. + let rules: Vec<_> = stylesheet + .rules + .0 + .iter() + .filter_map(|rule| match rule { + CssRule::Style(rule) => Some(rule), + _ => None, + }) + .collect(); + assert_eq!(rules.len(), 3); + for (rule, source) in rules.iter().zip(["/dependency.css", "/alias.css", "/entry.css"]) { + assert_eq!(stylesheet.sources[rule.loc.source_index as usize], source); + } + + stylesheet.to_css(PrinterOptions::default(), Some(&mut map)).unwrap(); + assert_eq!(map.mappings.len(), 3); + for (mapping, (source, content)) in map.mappings.iter().zip([ + ("/dependency.css", dependency), + ("/dependency.css", dependency), + ("/entry.css", entry), + ]) { + assert_eq!(map.get_source(mapping.source), Some(source)); + assert_eq!(map.get_source_content(mapping.source), Some(content)); + } + assert_eq!(map.mappings[0].source, map.mappings[1].source); + if prepopulated { + assert_eq!(map.get_source_content(100), Some("existing content")); + } + } +} + +#[test] +fn translates_bundler_indices_alongside_inline_remapping() { + let dependency = ".dependency { color: blue }"; + let fs = TestProvider(HashMap::from([ + ( + PathBuf::from("/entry.css"), + format!("@import 'dependency.css';\n.entry {{ color: red }}\n/*# sourceMappingURL={INPUT_MAP} */"), + ), + (PathBuf::from("/dependency.css"), dependency.into()), + ])); + let mut map = TestSourceMap::default(); + map.add_source("/existing.css"); + let mut bundler = Bundler::new(&fs, Some(&mut map), ParserOptions::default()); + let stylesheet = bundler.bundle(Path::new("/entry.css")).unwrap(); + stylesheet.to_css(PrinterOptions::default(), Some(&mut map)).unwrap(); + + assert_eq!(map.mappings.len(), 2); + let dependency_mapping = map.mappings[0]; + assert_eq!(map.get_source(dependency_mapping.source), Some("/dependency.css")); + assert_eq!(map.get_source_content(dependency_mapping.source), Some(dependency)); + let original = map.mappings[1]; + assert_eq!(map.get_source(original.source), Some("/original.scss")); + assert_eq!(map.get_source_content(original.source), Some(".original{color:red}")); + assert_eq!((original.original_line, original.original_column), (12, 3)); + assert!(!map.sources.iter().any(|(_, source, _)| source == "/entry.css")); +} From ebe2164e1a931e9ba7390725a30591b751723d36 Mon Sep 17 00:00:00 2001 From: AsyncIter Date: Wed, 14 Oct 2026 17:33:42 +0800 Subject: [PATCH 7/9] fix: adapt printer trait to latest master --- examples/custom_at_rule.rs | 26 +++++++++++++------------- napi/src/at_rule_parser.rs | 14 +++++++------- src/lib.rs | 2 +- src/traits.rs | 2 +- src/values/number.rs | 2 +- tests/test_custom_parser.rs | 12 ++++++------ 6 files changed, 29 insertions(+), 29 deletions(-) diff --git a/examples/custom_at_rule.rs b/examples/custom_at_rule.rs index 1a453e781..9d8f0274c 100644 --- a/examples/custom_at_rule.rs +++ b/examples/custom_at_rule.rs @@ -2,22 +2,22 @@ use std::{collections::HashMap, convert::Infallible}; use cssparser::*; use rspack_lightningcss::{ - declaration::DeclarationBlock, - error::PrinterError, - printer::PrinterTrait, - properties::custom::{Token, TokenOrValue}, - rules::{style::StyleRule, CssRule, CssRuleList, Location}, - selector::{Component, Selector}, - stylesheet::{ParserOptions, PrinterOptions, StyleSheet}, - targets::Browsers, - traits::{AtRuleParser, ToCss}, - values::{ + declaration::DeclarationBlock, + error::PrinterError, + printer::PrinterTrait, + properties::custom::{Token, TokenOrValue}, + rules::{style::StyleRule, CssRule, CssRuleList, Location}, + selector::{Component, Selector}, + stylesheet::{ParserOptions, PrinterOptions, StyleSheet}, + targets::Browsers, + traits::{AtRuleParser, ToCss}, + values::{ color::{CssColor, RGBA}, length::LengthValue, }, - vendor_prefix::VendorPrefix, - visit_types, - visitor::{Visit, VisitTypes, Visitor}, + vendor_prefix::VendorPrefix, + visit_types, + visitor::{Visit, VisitTypes, Visitor}, }; fn main() { diff --git a/napi/src/at_rule_parser.rs b/napi/src/at_rule_parser.rs index a1d7ab58c..e7e28079a 100644 --- a/napi/src/at_rule_parser.rs +++ b/napi/src/at_rule_parser.rs @@ -2,13 +2,13 @@ use std::collections::HashMap; use cssparser::*; use lightningcss::{ - declaration::DeclarationBlock, - error::ParserError, - printer::PrinterTrait, - rules::{CssRuleList, Location}, - stylesheet::ParserOptions, - traits::{AtRuleParser, ToCss}, - values::{ + declaration::DeclarationBlock, + error::ParserError, + printer::PrinterTrait, + rules::{CssRuleList, Location}, + stylesheet::ParserOptions, + traits::{AtRuleParser, ToCss}, + values::{ string::CowArcStr, syntax::{ParsedComponent, SyntaxString}, }, diff --git a/src/lib.rs b/src/lib.rs index 86ae21fea..e40b868cb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -338,7 +338,7 @@ mod tests { ) .unwrap(); stylesheet.minify(MinifyOptions::default()).unwrap(); - let res = stylesheet.to_css(PrinterOptions::default()).unwrap(); + let res = stylesheet.to_css(PrinterOptions::default(), None::<&mut ()>).unwrap(); let exports: Vec = res.exports.unwrap().into_keys().collect(); assert_eq!(exports, ["zebra", "apple", "mango"]); let references: Vec = res diff --git a/src/traits.rs b/src/traits.rs index da5781b3e..d5cc2fa93 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -3,7 +3,7 @@ use crate::context::PropertyHandlerContext; use crate::declaration::{DeclarationBlock, DeclarationList}; use crate::error::{ParserError, PrinterError}; -use crate::printer::{PrinterTrait, Printer}; +use crate::printer::{Printer, PrinterTrait}; use crate::properties::{Property, PropertyId}; use crate::stylesheet::{ParserOptions, PrinterOptions}; use crate::targets::{Browsers, Targets}; diff --git a/src/values/number.rs b/src/values/number.rs index f02d54eaf..4a1729486 100644 --- a/src/values/number.rs +++ b/src/values/number.rs @@ -60,7 +60,7 @@ impl<'i, T: ParseNumeric<'i>> Parse<'i> for NonNegative { } impl ToCss for NonNegative { - fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> { + fn to_css(&self, dest: &mut PrinterT) -> Result<(), PrinterError> { self.0.to_css(dest) } } diff --git a/tests/test_custom_parser.rs b/tests/test_custom_parser.rs index 74cef6d39..86de2e6aa 100644 --- a/tests/test_custom_parser.rs +++ b/tests/test_custom_parser.rs @@ -1,11 +1,11 @@ use cssparser::*; use rspack_lightningcss::{ - declaration::DeclarationBlock, - error::{ParserError, PrinterError}, - printer::PrinterTrait, - stylesheet::{ParserOptions, PrinterOptions, StyleSheet}, - traits::{AtRuleParser, Parse, ToCss}, - values::ident::Ident, + declaration::DeclarationBlock, + error::{ParserError, PrinterError}, + printer::PrinterTrait, + stylesheet::{ParserOptions, PrinterOptions, StyleSheet}, + traits::{AtRuleParser, Parse, ToCss}, + values::ident::Ident, }; fn minify_test(source: &str, expected: &str) { From b3da87666590d188ae7afec936ee8f382902deb5 Mon Sep 17 00:00:00 2001 From: AsyncIter Date: Wed, 14 Oct 2026 17:38:27 +0800 Subject: [PATCH 8/9] fix: preserve source map positions through printer trait --- examples/custom_at_rule.rs | 3 ++- src/printer.rs | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/examples/custom_at_rule.rs b/examples/custom_at_rule.rs index 9d8f0274c..1a8b5ead3 100644 --- a/examples/custom_at_rule.rs +++ b/examples/custom_at_rule.rs @@ -307,7 +307,8 @@ impl ToCss for AtRule { TailwindDirective::Utilities => "TAILWIND UTILITIES HERE", TailwindDirective::Variants => "TAILWIND VARIANTS HERE", }; - dest.write_str(directive) + dest.write_str(directive)?; + Ok(()) } AtRule::Apply(_) => Ok(()), } diff --git a/src/printer.rs b/src/printer.rs index 60b04d945..b21177a57 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -618,6 +618,10 @@ impl<'a, 'c, W: std::fmt::Write + Sized, S: SourceMap> std::fmt::Write for Print self.state.generated_col += s.len() as u32; self.dest.write_str(s) } + + fn write_char(&mut self, c: char) -> std::fmt::Result { + Printer::write_char(self, c).map_err(|_| std::fmt::Error) + } } mod private { From 1aeaa9dea16d6630ee6c1ad38fe8fd06176b080e Mon Sep 17 00:00:00 2001 From: AsyncIter Date: Wed, 21 Oct 2026 16:37:52 +0800 Subject: [PATCH 9/9] fix: adapt custom source map tests to rspack crate name --- benches/common/mod.rs | 2 +- tests/test_bundler_source_map.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/benches/common/mod.rs b/benches/common/mod.rs index 51298a912..3c6595546 100644 --- a/benches/common/mod.rs +++ b/benches/common/mod.rs @@ -1,6 +1,6 @@ use criterion::black_box; -use rspack_lightningcss::stylesheet::{MinifyOptions, ParserOptions, PrinterOptions, StyleSheet}; use parcel_sourcemap::SourceMap; +use rspack_lightningcss::stylesheet::{MinifyOptions, ParserOptions, PrinterOptions, StyleSheet}; use std::fmt::Write; pub struct Fixture { diff --git a/tests/test_bundler_source_map.rs b/tests/test_bundler_source_map.rs index d7c395c8b..856e9d742 100644 --- a/tests/test_bundler_source_map.rs +++ b/tests/test_bundler_source_map.rs @@ -1,6 +1,6 @@ #![cfg(feature = "bundler")] -use lightningcss::{ +use rspack_lightningcss::{ bundler::{Bundler, ResolveResult, SourceProvider}, printer::{OriginalLocation, SourceMap}, rules::CssRule,