mirror of
https://github.com/swc-project/swc.git
synced 2024-12-25 22:56:11 +03:00
fix(html/minifier): Compress more complex attributes (#5024)
This commit is contained in:
parent
29522b60a5
commit
c61babd9af
@ -204,8 +204,6 @@ static ALLOW_TO_TRIM_HTML_ATTRIBUTES: &[(&str, &str)] = &[
|
||||
("object", "usemap"),
|
||||
];
|
||||
|
||||
static COMMA_SEPARATED_GLOBAL_ATTRIBUTES: &[&str] = &["class"];
|
||||
|
||||
static COMMA_SEPARATED_HTML_ATTRIBUTES: &[(&str, &str)] = &[
|
||||
("img", "srcset"),
|
||||
("source", "srcset"),
|
||||
@ -218,6 +216,7 @@ static COMMA_SEPARATED_HTML_ATTRIBUTES: &[(&str, &str)] = &[
|
||||
|
||||
static SPACE_SEPARATED_GLOBAL_ATTRIBUTES: &[&str] = &[
|
||||
"class",
|
||||
"part",
|
||||
"itemtype",
|
||||
"itemref",
|
||||
"itemprop",
|
||||
@ -296,28 +295,81 @@ impl Minifier {
|
||||
HTML_BOOLEAN_ATTRIBUTES.contains(&name)
|
||||
}
|
||||
|
||||
fn is_global_trimable_attribute(&self, name: &str) -> bool {
|
||||
ALLOW_TO_TRIM_GLOBAL_ATTRIBUTES.contains(&name)
|
||||
fn is_trimable_separated_attribute(&self, element: &Element, attribute_name: &str) -> bool {
|
||||
if ALLOW_TO_TRIM_GLOBAL_ATTRIBUTES.contains(&attribute_name) {
|
||||
return true;
|
||||
}
|
||||
|
||||
match element.namespace {
|
||||
Namespace::HTML => {
|
||||
ALLOW_TO_TRIM_HTML_ATTRIBUTES.contains(&(&element.tag_name, attribute_name))
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_html_trimable_attribute(&self, tag_name: &str, attribute_name: &str) -> bool {
|
||||
ALLOW_TO_TRIM_HTML_ATTRIBUTES.contains(&(tag_name, attribute_name))
|
||||
fn is_comma_separated_attribute(&self, element: &Element, attribute_name: &str) -> bool {
|
||||
match element.namespace {
|
||||
Namespace::HTML => match attribute_name {
|
||||
"content"
|
||||
if &*element.tag_name == "meta"
|
||||
&& (self.element_has_attribute_with_value(
|
||||
element,
|
||||
"name",
|
||||
&["viewport", "keywords"],
|
||||
)) =>
|
||||
{
|
||||
true
|
||||
}
|
||||
"imagesrcset"
|
||||
if &*element.tag_name == "link"
|
||||
&& self.element_has_attribute_with_value(element, "rel", &["preload"]) =>
|
||||
{
|
||||
true
|
||||
}
|
||||
"imagesizes"
|
||||
if &*element.tag_name == "link"
|
||||
&& self.element_has_attribute_with_value(element, "rel", &["preload"]) =>
|
||||
{
|
||||
true
|
||||
}
|
||||
"accept"
|
||||
if &*element.tag_name == "input"
|
||||
&& self.element_has_attribute_with_value(element, "type", &["file"]) =>
|
||||
{
|
||||
true
|
||||
}
|
||||
_ => COMMA_SEPARATED_HTML_ATTRIBUTES.contains(&(&element.tag_name, attribute_name)),
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_global_comma_separated_attribute(&self, name: &str) -> bool {
|
||||
COMMA_SEPARATED_GLOBAL_ATTRIBUTES.contains(&name)
|
||||
fn is_space_separated_attribute(&self, element: &Element, attribute_name: &str) -> bool {
|
||||
if SPACE_SEPARATED_GLOBAL_ATTRIBUTES.contains(&attribute_name) {
|
||||
return true;
|
||||
}
|
||||
|
||||
match element.namespace {
|
||||
Namespace::HTML => {
|
||||
SPACE_SEPARATED_HTML_ATTRIBUTES.contains(&(&element.tag_name, attribute_name))
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_html_comma_separated_attribute(&self, tag_name: &str, attribute_name: &str) -> bool {
|
||||
COMMA_SEPARATED_HTML_ATTRIBUTES.contains(&(tag_name, attribute_name))
|
||||
}
|
||||
|
||||
fn is_global_space_separated_attribute(&self, name: &str) -> bool {
|
||||
SPACE_SEPARATED_GLOBAL_ATTRIBUTES.contains(&name)
|
||||
}
|
||||
|
||||
fn is_html_space_separated_attribute(&self, tag_name: &str, attribute_name: &str) -> bool {
|
||||
SPACE_SEPARATED_HTML_ATTRIBUTES.contains(&(tag_name, attribute_name))
|
||||
fn element_has_attribute_with_value(
|
||||
&self,
|
||||
element: &Element,
|
||||
attribute_name: &str,
|
||||
attribute_value: &[&str],
|
||||
) -> bool {
|
||||
element.attributes.iter().any(|attribute| {
|
||||
&*attribute.name == attribute_name
|
||||
&& attribute.value.is_some()
|
||||
&& attribute_value
|
||||
.contains(&&*attribute.value.as_ref().unwrap().to_ascii_lowercase())
|
||||
})
|
||||
}
|
||||
|
||||
fn is_default_attribute_value(
|
||||
@ -1012,12 +1064,7 @@ impl VisitMut for Minifier {
|
||||
n.value = None;
|
||||
|
||||
return;
|
||||
} else if self.is_global_space_separated_attribute(&n.name)
|
||||
|| (is_element_html_namespace
|
||||
&& self.is_html_space_separated_attribute(
|
||||
&*self.current_element.as_ref().unwrap().tag_name,
|
||||
&n.name,
|
||||
))
|
||||
} else if self.is_space_separated_attribute(self.current_element.as_ref().unwrap(), &n.name)
|
||||
{
|
||||
let mut values = value.split_whitespace().collect::<Vec<_>>();
|
||||
|
||||
@ -1026,29 +1073,7 @@ impl VisitMut for Minifier {
|
||||
}
|
||||
|
||||
value = values.join(" ");
|
||||
} else if self.is_global_comma_separated_attribute(&n.name)
|
||||
|| (is_element_html_namespace
|
||||
&& ((&n.name == "content"
|
||||
&& self
|
||||
.current_element
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.attributes
|
||||
.iter()
|
||||
.any(|attribute| match &*attribute.name.to_ascii_lowercase() {
|
||||
"name"
|
||||
if attribute.value.is_some()
|
||||
&& &*attribute.value.as_ref().unwrap().to_ascii_lowercase()
|
||||
== "viewport" =>
|
||||
{
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}))
|
||||
|| self.is_html_comma_separated_attribute(
|
||||
&*self.current_element.as_ref().unwrap().tag_name,
|
||||
&n.name,
|
||||
)))
|
||||
} else if self.is_comma_separated_attribute(self.current_element.as_ref().unwrap(), &n.name)
|
||||
{
|
||||
let values = value.trim().split(',');
|
||||
|
||||
@ -1059,12 +1084,8 @@ impl VisitMut for Minifier {
|
||||
}
|
||||
|
||||
value = new_values.join(",");
|
||||
} else if self.is_global_trimable_attribute(&n.name)
|
||||
|| (is_element_html_namespace
|
||||
&& self.is_html_trimable_attribute(
|
||||
&*self.current_element.as_ref().unwrap().tag_name,
|
||||
&n.name,
|
||||
))
|
||||
} else if self
|
||||
.is_trimable_separated_attribute(self.current_element.as_ref().unwrap(), &n.name)
|
||||
{
|
||||
value = value.trim().to_string();
|
||||
} else if is_element_html_namespace && &n.name == "contenteditable" && value == "true" {
|
||||
@ -1072,22 +1093,11 @@ impl VisitMut for Minifier {
|
||||
|
||||
return;
|
||||
} else if &n.name == "content"
|
||||
&& self
|
||||
.current_element
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.attributes
|
||||
.iter()
|
||||
.any(|attribute| match &*attribute.name.to_ascii_lowercase() {
|
||||
"http-equiv"
|
||||
if attribute.value.is_some()
|
||||
&& &*attribute.value.as_ref().unwrap().to_ascii_lowercase()
|
||||
== "content-security-policy" =>
|
||||
{
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
})
|
||||
&& self.element_has_attribute_with_value(
|
||||
self.current_element.as_ref().unwrap(),
|
||||
"http-equiv",
|
||||
&["content-security-policy"],
|
||||
)
|
||||
{
|
||||
let values = value.trim().split(';');
|
||||
|
||||
|
@ -0,0 +1,11 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Document</title>
|
||||
</head>
|
||||
<body>
|
||||
<input type="file"
|
||||
id="avatar" name="avatar"
|
||||
accept="image/png, image/jpeg">
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,3 @@
|
||||
<!doctype html><html lang=en><title>Document</title><body>
|
||||
<input type=file id=avatar name=avatar accept=image/png,image/jpeg>
|
||||
|
@ -0,0 +1,15 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Document</title>
|
||||
<meta name="keywords" content="
|
||||
british , type face , font , fonts , highway , highways
|
||||
">
|
||||
<meta name="keywords" content="
|
||||
a ,b,,d d
|
||||
">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,3 @@
|
||||
<!doctype html><html lang=en><title>Document</title><meta name=keywords content="british,type face,font,fonts,highway,highways"><meta name=keywords content="a,b,,d d"><body>
|
||||
|
||||
|
@ -0,0 +1,28 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Document</title>
|
||||
<link rel="preload" as="image"
|
||||
imagesrcset="dog-cropped-1x.jpg, dog-cropped-2x.jpg 2x"
|
||||
media="(max-width: 800px)">
|
||||
<link rel="preload" as="image"
|
||||
imagesrcset="dog-wide-1x.jpg, dog-wide-2x.jpg 2x"
|
||||
media="(min-width: 801px)">
|
||||
<link rel="preload" as="image"
|
||||
imagesrcset="wolf_400px.jpg 400w, wolf_800px.jpg 800w, wolf_1600px.jpg 1600w"
|
||||
imagesizes="50vw">
|
||||
<link rel="preload" as="image"
|
||||
imagesrcset="wolf_400px.jpg 400w, wolf_800px.jpg 800w, wolf_1600px.jpg 1600w"
|
||||
imagesizes="(max-width: 600px) 480px,
|
||||
800px">
|
||||
</head>
|
||||
<body>
|
||||
<div>test</div>
|
||||
<img srcset="elva-fairy-480w.jpg 480w,
|
||||
elva-fairy-800w.jpg 800w"
|
||||
sizes="(max-width: 600px) 480px,
|
||||
800px"
|
||||
src="elva-fairy-800w.jpg"
|
||||
alt="Elva dressed as a fairy">
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,4 @@
|
||||
<!doctype html><html lang=en><title>Document</title><link rel=preload as=image imagesrcset="dog-cropped-1x.jpg,dog-cropped-2x.jpg 2x" media="(max-width: 800px)"><link rel=preload as=image imagesrcset="dog-wide-1x.jpg,dog-wide-2x.jpg 2x" media="(min-width: 801px)"><link rel=preload as=image imagesrcset="wolf_400px.jpg 400w,wolf_800px.jpg 800w,wolf_1600px.jpg 1600w" imagesizes=50vw><link rel=preload as=image imagesrcset="wolf_400px.jpg 400w,wolf_800px.jpg 800w,wolf_1600px.jpg 1600w" imagesizes="(max-width: 600px) 480px,800px"><body>
|
||||
<div>test</div>
|
||||
<img srcset="elva-fairy-480w.jpg 480w,elva-fairy-800w.jpg 800w" sizes="(max-width: 600px) 480px,800px" src=elva-fairy-800w.jpg alt="Elva dressed as a fairy">
|
||||
|
@ -0,0 +1,32 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Document</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<style>
|
||||
c-e::part(textspan) { color: red; }
|
||||
</style>
|
||||
|
||||
<template id="c-e-template">
|
||||
<span part="
|
||||
textspan
|
||||
|
||||
a
|
||||
|
||||
|
||||
b
|
||||
|
||||
|
||||
|
||||
c
|
||||
|
||||
|
||||
">This text will be red</span>
|
||||
</template>
|
||||
|
||||
<c-e></c-e>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,11 @@
|
||||
<!doctype html><html lang=en><title>Document</title><body>
|
||||
|
||||
<style>c-e::part(textspan){color:red}</style>
|
||||
|
||||
<template id=c-e-template>
|
||||
<span part="textspan a b c">This text will be red</span>
|
||||
</template>
|
||||
|
||||
<c-e></c-e>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user