use crate::css_parser as css;
use bun_alloc::Arena as Bump;
use bun_alloc::ArenaVecExt as _;
pub use css::Error;
use css::{CssResult as Result, PrintErr, Printer};
use crate::css_properties::align::AlignHandler;
use crate::css_properties::background::BackgroundHandler;
use crate::css_properties::border::BorderHandler;
use crate::css_properties::box_shadow::BoxShadowHandler;
use crate::css_properties::custom::CustomPropertyName;
use crate::css_properties::flex::FlexHandler;
use crate::css_properties::font::FontHandler;
use crate::css_properties::margin_padding::{
InsetHandler, MarginHandler, PaddingHandler, ScrollMarginHandler,
};
use crate::css_properties::prefix_handler::FallbackHandler;
use crate::css_properties::size::SizeHandler;
use crate::css_properties::text::Direction;
use crate::css_properties::transform::TransformHandler;
use crate::css_properties::transition::TransitionHandler;
use crate::css_properties::ui::ColorSchemeHandler;
pub type DeclarationList<'bump> = bun_alloc::ArenaVec<'bump, css::Property>;
pub struct DeclarationBlock<'bump> {
pub important_declarations: DeclarationList<'bump>,
pub declarations: DeclarationList<'bump>,
}
pub struct DebugFmt<'a, 'bump>(&'a DeclarationBlock<'bump>);
impl<'a, 'bump> core::fmt::Display for DebugFmt<'a, 'bump> {
fn fmt(&self, writer: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let bump = Bump::new();
let mut arraylist: Vec<u8> = Vec::new();
let symbols = bun_ast::symbol::Map::init_list(Default::default());
let mut printer = css::Printer::new(
&bump,
bun_alloc::ArenaVec::<u8>::new_in(&bump),
&mut arraylist,
&css::PrinterOptions::default(),
None,
None,
&symbols,
);
let res = self.0.to_css(&mut printer);
drop(printer);
match res {
Ok(()) => {}
Err(e) => {
return writeln!(writer, "<error writing declaration block: {}>", e.name());
}
}
write!(writer, "{}", bstr::BStr::new(&arraylist))
}
}
impl<'bump> DeclarationBlock<'bump> {
pub fn debug(&self) -> DebugFmt<'_, 'bump> {
DebugFmt(self)
}
pub fn is_empty(&self) -> bool {
self.declarations.is_empty() && self.important_declarations.is_empty()
}
pub fn len(&self) -> usize {
self.declarations.len() + self.important_declarations.len()
}
pub fn new_in(bump: &'bump Bump) -> Self {
Self {
important_declarations: DeclarationList::new_in(bump),
declarations: DeclarationList::new_in(bump),
}
}
pub fn minify(
&mut self,
handler: &mut DeclarationHandler<'bump>,
important_handler: &mut DeclarationHandler<'bump>,
context: &mut css::PropertyHandlerContext,
) {
let bump: &'bump Bump = handler.decls.bump();
#[inline]
fn handle<'bump>(
decls: &mut DeclarationList<'bump>,
ctx: &mut css::PropertyHandlerContext,
hndlr: &mut DeclarationHandler<'bump>,
important: bool,
) {
for prop in decls.iter_mut() {
ctx.is_important = important;
let handled = hndlr.handle_property(prop, ctx);
if !handled {
hndlr
.decls
.push(core::mem::replace(prop, placeholder_property()));
}
}
}
handle(
&mut self.important_declarations,
context,
important_handler,
true,
);
handle(&mut self.declarations, context, handler, false);
handler.finalize(context);
important_handler.finalize(context);
self.important_declarations =
core::mem::replace(&mut important_handler.decls, DeclarationList::new_in(bump));
self.declarations = core::mem::replace(&mut handler.decls, DeclarationList::new_in(bump));
}
}
#[inline(always)]
fn placeholder_property() -> css::Property {
css::Property::All(crate::css_properties::CSSWideKeyword::RevertLayer)
}
impl<'bump> DeclarationBlock<'bump> {
pub fn to_css(&self, dest: &mut Printer) -> core::result::Result<(), PrintErr> {
let length = self.len();
let mut i: usize = 0;
for decl in self.declarations.iter() {
decl.to_css(dest, false)?;
if i != length - 1 {
dest.write_char(b';')?;
dest.whitespace()?;
}
i += 1;
}
for decl in self.important_declarations.iter() {
decl.to_css(dest, true)?;
if i != length - 1 {
dest.write_char(b';')?;
dest.whitespace()?;
}
i += 1;
}
Ok(())
}
pub fn to_css_block(&self, dest: &mut Printer) -> core::result::Result<(), PrintErr> {
dest.whitespace()?;
dest.write_char(b'{')?;
dest.indent();
let mut i: usize = 0;
let length = self.len();
for decl in self.declarations.iter() {
dest.newline()?;
decl.to_css(dest, false)?;
if i != length - 1 || !dest.minify {
dest.write_char(b';')?;
}
i += 1;
}
for decl in self.important_declarations.iter() {
dest.newline()?;
decl.to_css(dest, true)?;
if i != length - 1 || !dest.minify {
dest.write_char(b';')?;
}
i += 1;
}
dest.dedent();
dest.newline()?;
dest.write_char(b'}')
}
}
impl DeclarationBlock<'static> {
pub fn parse(
input: &mut css::Parser,
options: &css::ParserOptions,
) -> Result<DeclarationBlock<'static>> {
let bump: &'static Bump = unsafe { bun_ptr::detach_lifetime_ref(input.arena()) };
let mut important_declarations = DeclarationList::new_in(bump);
let mut declarations = DeclarationList::new_in(bump);
let mut decl_parser = PropertyDeclarationParser {
important_declarations: &mut important_declarations,
declarations: &mut declarations,
options,
};
let mut parser = css::RuleBodyParser::new(input, &mut decl_parser);
while let Some(res) = parser.next() {
if let Err(e) = res {
if options.error_recovery {
options.warn(&e);
continue;
}
return Err(e);
}
}
Ok(DeclarationBlock {
important_declarations,
declarations,
})
}
}
impl<'bump> DeclarationBlock<'bump> {
pub fn hash_property_ids(&self, hasher: &mut bun_wyhash::Wyhash) {
use std::hash::Hash;
for decl in self.declarations.iter() {
decl.property_id().hash(hasher);
}
for decl in self.important_declarations.iter() {
decl.property_id().hash(hasher);
}
}
pub fn eql(&self, other: &Self) -> bool {
if self.declarations.len() != other.declarations.len()
|| self.important_declarations.len() != other.important_declarations.len()
{
return false;
}
self.declarations
.iter()
.zip(other.declarations.iter())
.all(|(a, b)| a.eql(b))
&& self
.important_declarations
.iter()
.zip(other.important_declarations.iter())
.all(|(a, b)| a.eql(b))
}
pub fn deep_clone(&self, bump: &'bump Bump) -> Self {
Self {
important_declarations: bun_alloc::vec_from_iter_in(
self.important_declarations
.iter()
.map(|p| p.deep_clone(bump)),
bump,
),
declarations: bun_alloc::vec_from_iter_in(
self.declarations.iter().map(|p| p.deep_clone(bump)),
bump,
),
}
}
}
pub(crate) struct PropertyDeclarationParser<'a, 'bump> {
pub important_declarations: &'a mut DeclarationList<'bump>,
pub declarations: &'a mut DeclarationList<'bump>,
pub options: &'a css::ParserOptions<'a>,
}
impl<'a, 'bump> css::AtRuleParser for PropertyDeclarationParser<'a, 'bump> {
type Prelude = ();
type AtRule = ();
fn parse_prelude(
_this: &mut Self,
name: &[u8],
input: &mut css::Parser,
) -> Result<Self::Prelude> {
Err(input.new_error(css::BasicParseErrorKind::at_rule_invalid(name)))
}
fn parse_block(
_this: &mut Self,
_: Self::Prelude,
_: &css::ParserState,
input: &mut css::Parser,
) -> Result<Self::AtRule> {
Err(input.new_error(css::BasicParseErrorKind::at_rule_body_invalid))
}
fn rule_without_block(
_this: &mut Self,
_: Self::Prelude,
_: &css::ParserState,
) -> css::Maybe<Self::AtRule, ()> {
Err(())
}
}
impl<'a, 'bump> css::QualifiedRuleParser for PropertyDeclarationParser<'a, 'bump> {
type Prelude = ();
type QualifiedRule = ();
fn parse_prelude(_this: &mut Self, input: &mut css::Parser) -> Result<Self::Prelude> {
Err(input.new_error(css::BasicParseErrorKind::qualified_rule_invalid))
}
fn parse_block(
_this: &mut Self,
_prelude: Self::Prelude,
_start: &css::ParserState,
input: &mut css::Parser,
) -> Result<Self::QualifiedRule> {
Err(input.new_error(css::BasicParseErrorKind::qualified_rule_invalid))
}
}
impl<'a, 'bump> css::DeclarationParser for PropertyDeclarationParser<'a, 'bump> {
type Declaration = ();
fn parse_value(
this: &mut Self,
name: &[u8],
input: &mut css::Parser,
) -> Result<Self::Declaration> {
parse_declaration(
name,
input,
this.declarations,
this.important_declarations,
this.options,
)
}
}
impl<'a, 'bump> css::RuleBodyItemParser for PropertyDeclarationParser<'a, 'bump> {
fn parse_qualified(_this: &Self) -> bool {
false
}
fn parse_declarations(_this: &Self) -> bool {
true
}
}
pub fn parse_declaration<'bump>(
name: &[u8],
input: &mut css::Parser,
declarations: &mut DeclarationList<'bump>,
important_declarations: &mut DeclarationList<'bump>,
options: &css::ParserOptions,
) -> Result<()> {
parse_declaration_impl(
name,
input,
declarations,
important_declarations,
options,
&mut css::NoComposesCtx,
)
}
pub fn parse_declaration_impl<'bump, C>(
name: &[u8],
input: &mut css::Parser,
declarations: &mut DeclarationList<'bump>,
important_declarations: &mut DeclarationList<'bump>,
options: &css::ParserOptions,
composes_ctx: &mut C,
) -> Result<()>
where
C: css::ComposesCtx + ?Sized,
{
let property_id = css::PropertyId::from_string(name);
let mut delimiters = css::Delimiters::BANG;
if !matches!(
property_id,
css::PropertyId::Custom(CustomPropertyName::Custom(_))
) {
delimiters |= css::Delimiters::CURLY_BRACKET;
}
let source_location = input.current_source_location();
let mut property = input.parse_until_before(delimiters, |input2: &mut css::Parser| {
css::Property::parse(property_id, input2, options)
})?;
let important = input
.try_parse(|i: &mut css::Parser| -> Result<()> {
i.expect_delim(b'!')?;
i.expect_ident_matching(b"important")
})
.is_ok();
input.expect_exhausted()?;
if input.flags.css_modules() {
if let css::Property::Composes(composes) = &mut property {
match composes_ctx.composes_state() {
css::ComposesState::DisallowEntirely => {}
css::ComposesState::Allow(_) => {
composes_ctx.record_composes(composes);
}
css::ComposesState::DisallowNested(info) => {
options.warn_fmt(
format_args!("\"composes\" is not allowed inside nested selectors"),
info.line,
info.column,
);
}
css::ComposesState::DisallowNotSingleClass(info) => {
let _ = info;
options.warn_fmt(
format_args!("\"composes\" only works inside single class selectors"),
source_location.line,
source_location.column,
);
}
}
}
}
if important {
important_declarations.push(property);
} else {
declarations.push(property);
}
Ok(())
}
pub struct DeclarationHandler<'bump> {
pub background: BackgroundHandler,
pub border: BorderHandler,
pub flex: FlexHandler,
pub align: AlignHandler,
pub size: SizeHandler,
pub margin: MarginHandler,
pub padding: PaddingHandler,
pub scroll_margin: ScrollMarginHandler,
pub transition: TransitionHandler,
pub font: FontHandler,
pub inset: InsetHandler,
pub transform: TransformHandler,
pub box_shadow: BoxShadowHandler,
pub color_scheme: ColorSchemeHandler,
pub fallback: FallbackHandler,
pub direction: Option<Direction>,
pub decls: DeclarationList<'bump>,
}
impl<'bump> DeclarationHandler<'bump> {
pub fn finalize(&mut self, context: &mut css::PropertyHandlerContext) {
if let Some(direction) = self.direction.take() {
self.decls.push(css::Property::Direction(direction));
}
self.background.finalize(&mut self.decls, context);
self.border.finalize(&mut self.decls, context);
self.flex.finalize(&mut self.decls, context);
self.align.finalize(&mut self.decls, context);
self.size.finalize(&mut self.decls, context);
self.margin.finalize(&mut self.decls, context);
self.padding.finalize(&mut self.decls, context);
self.scroll_margin.finalize(&mut self.decls, context);
self.transition.finalize(&mut self.decls, context);
self.font.finalize(&mut self.decls, context);
self.inset.finalize(&mut self.decls, context);
self.transform.finalize(&mut self.decls, context);
self.box_shadow.finalize(&mut self.decls, context);
self.color_scheme.finalize(&mut self.decls, context);
self.fallback.finalize(&mut self.decls, context);
}
pub fn handle_property(
&mut self,
property: &css::Property,
context: &mut css::PropertyHandlerContext,
) -> bool {
self.background
.handle_property(property, &mut self.decls, context)
|| self
.border
.handle_property(property, &mut self.decls, context)
|| self
.flex
.handle_property(property, &mut self.decls, context)
|| self
.align
.handle_property(property, &mut self.decls, context)
|| self
.size
.handle_property(property, &mut self.decls, context)
|| self
.margin
.handle_property(property, &mut self.decls, context)
|| self
.padding
.handle_property(property, &mut self.decls, context)
|| self
.scroll_margin
.handle_property(property, &mut self.decls, context)
|| self
.transition
.handle_property(property, &mut self.decls, context)
|| self
.font
.handle_property(property, &mut self.decls, context)
|| self
.inset
.handle_property(property, &mut self.decls, context)
|| self
.transform
.handle_property(property, &mut self.decls, context)
|| self
.box_shadow
.handle_property(property, &mut self.decls, context)
|| self
.color_scheme
.handle_property(property, &mut self.decls, context)
|| self
.fallback
.handle_property(property, &mut self.decls, context)
}
pub fn new(bump: &'bump Bump) -> Self {
Self {
background: Default::default(),
border: Default::default(),
flex: Default::default(),
align: Default::default(),
size: Default::default(),
margin: Default::default(),
padding: Default::default(),
scroll_margin: Default::default(),
transition: Default::default(),
font: Default::default(),
inset: Default::default(),
transform: Default::default(),
box_shadow: Default::default(),
color_scheme: Default::default(),
fallback: Default::default(),
direction: None,
decls: DeclarationList::new_in(bump),
}
}
}