minify-html/src/minify/element.rs

81 lines
2.5 KiB
Rust
Raw Normal View History

2021-08-06 02:17:45 -04:00
use std::collections::HashMap;
2021-08-06 03:54:23 -04:00
use crate::ast::{ElementClosingTag, NodeData};
2021-08-06 02:17:45 -04:00
use crate::cfg::Cfg;
2021-08-06 03:54:23 -04:00
use crate::minify::attr::{minify_attr_val, AttrType};
2021-08-06 02:17:45 -04:00
use crate::minify::content::minify_content;
2021-08-06 03:33:56 -04:00
use crate::spec::tag::ns::Namespace;
2021-08-06 02:17:45 -04:00
use crate::spec::tag::omission::{can_omit_as_before, can_omit_as_last_node};
pub fn minify_element(
cfg: &Cfg,
out: &mut Vec<u8>,
2021-08-06 03:33:56 -04:00
descendant_of_pre: bool,
ns: Namespace,
2021-08-06 02:17:45 -04:00
// Use an empty slice if none.
parent: &[u8],
// Use an empty slice if the next element or text sibling node is not an element.
next_sibling_as_element_tag_name: &[u8],
2021-08-06 03:33:56 -04:00
// If the last node of the parent is an element and it's this one.
2021-08-06 02:17:45 -04:00
is_last_child_text_or_element_node: bool,
tag_name: &[u8],
2021-08-06 03:33:56 -04:00
attributes: HashMap<Vec<u8>, Vec<u8>>,
2021-08-06 02:17:45 -04:00
closing_tag: ElementClosingTag,
2021-08-06 03:33:56 -04:00
children: Vec<NodeData>,
2021-08-06 09:18:45 -04:00
) {
2021-08-06 02:19:36 -04:00
let can_omit_closing_tag = cfg.omit_closing_tags
&& (can_omit_as_before(tag_name, next_sibling_as_element_tag_name)
2021-08-06 02:19:36 -04:00
|| (is_last_child_text_or_element_node && can_omit_as_last_node(parent, tag_name)));
2021-08-06 02:17:45 -04:00
out.push(b'<');
out.extend_from_slice(tag_name);
2021-08-06 03:54:23 -04:00
let mut last_attr = AttrType::NoValue;
let mut attrs_sorted = attributes.into_iter().collect::<Vec<_>>();
attrs_sorted.sort_unstable_by(|a, b| a.0.cmp(&b.0));
for (name, value) in attrs_sorted {
let min = minify_attr_val(ns, tag_name, &name, value);
if min.typ() == AttrType::Redundant {
continue;
};
2021-08-06 03:54:23 -04:00
if !cfg.remove_spaces_between_attributes || last_attr != AttrType::Quoted {
2021-08-06 02:17:45 -04:00
out.push(b' ');
};
2021-08-06 03:33:56 -04:00
out.extend_from_slice(&name);
if min.len() == 0 {
2021-08-06 03:54:23 -04:00
last_attr = AttrType::NoValue;
} else {
2021-08-06 02:17:45 -04:00
out.push(b'=');
2021-08-06 03:33:56 -04:00
min.out(out);
last_attr = min.typ();
2021-08-06 02:17:45 -04:00
};
2021-08-06 02:19:36 -04:00
}
2021-08-06 02:17:45 -04:00
if closing_tag == ElementClosingTag::SelfClosing {
if last_attr == AttrType::Unquoted {
out.push(b' ');
};
out.push(b'/');
};
out.push(b'>');
if closing_tag == ElementClosingTag::SelfClosing || closing_tag == ElementClosingTag::Void {
debug_assert!(children.is_empty());
return;
};
2021-08-06 03:33:56 -04:00
minify_content(
cfg,
out,
descendant_of_pre || (ns == Namespace::Html && tag_name == b"pre"),
tag_name,
children,
);
2021-08-06 02:17:45 -04:00
2021-08-06 02:19:36 -04:00
if closing_tag != ElementClosingTag::Present || (cfg.omit_closing_tags && can_omit_closing_tag)
{
2021-08-06 02:17:45 -04:00
return;
};
out.extend_from_slice(b"</");
out.extend_from_slice(tag_name);
out.push(b'>');
}