2021-08-06 02:17:45 -04:00
|
|
|
use std::collections::HashMap;
|
|
|
|
|
|
|
|
use crate::ast::{ElementClosingTag, NodeData, ScriptOrStyleLang};
|
|
|
|
use crate::cfg::Cfg;
|
|
|
|
use crate::gen::codepoints::TAG_NAME_CHAR;
|
2021-08-06 03:33:56 -04:00
|
|
|
use crate::minify::attr::{minify_attr_val, AttrType, AttrValMinified};
|
2021-08-06 02:17:45 -04:00
|
|
|
use crate::minify::bang::minify_bang;
|
|
|
|
use crate::minify::comment::minify_comment;
|
|
|
|
use crate::minify::content::minify_content;
|
|
|
|
use crate::minify::css::minify_css;
|
|
|
|
use crate::minify::instruction::minify_instruction;
|
|
|
|
use crate::minify::js::minify_js;
|
|
|
|
use crate::pattern::Replacer;
|
|
|
|
use crate::spec::entity::encode::encode_ampersands;
|
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};
|
2021-08-06 02:19:36 -04:00
|
|
|
use crate::spec::tag::EMPTY_TAG_NAME;
|
2021-08-06 02:17:45 -04:00
|
|
|
|
|
|
|
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 none.
|
|
|
|
previous_sibling_element: &[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 02:17:45 -04:00
|
|
|
) -> () {
|
2021-08-06 02:19:36 -04:00
|
|
|
let can_omit_closing_tag = cfg.omit_closing_tags
|
|
|
|
&& (can_omit_as_before(previous_sibling_element, tag_name)
|
|
|
|
|| (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);
|
|
|
|
let mut last_attr = AttrType::None;
|
|
|
|
for (name, value) in attributes {
|
|
|
|
if !cfg.remove_spaces_between_attributes || last_attr == AttrType::Unquoted {
|
|
|
|
out.push(b' ');
|
|
|
|
};
|
2021-08-06 03:33:56 -04:00
|
|
|
out.extend_from_slice(&name);
|
2021-08-06 02:17:45 -04:00
|
|
|
if !value.is_empty() {
|
2021-08-06 03:33:56 -04:00
|
|
|
let min = minify_attr_val(&encode_ampersands(&value, true));
|
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'>');
|
|
|
|
}
|