diff --git a/crates/swc_html_minifier/src/lib.rs b/crates/swc_html_minifier/src/lib.rs
index 1c17801797c0..af0f67fa8098 100644
--- a/crates/swc_html_minifier/src/lib.rs
+++ b/crates/swc_html_minifier/src/lib.rs
@@ -578,6 +578,36 @@ impl Minifier<'_> {
})
}
+ fn is_type_text_javascript(&self, value: &JsWord) -> bool {
+ let value = value.trim().to_ascii_lowercase();
+ let value = if let Some(next) = value.split(';').next() {
+ next
+ } else {
+ &value
+ };
+
+ match value {
+ // Legacy JavaScript MIME types
+ "application/javascript"
+ | "application/ecmascript"
+ | "application/x-ecmascript"
+ | "application/x-javascript"
+ | "text/ecmascript"
+ | "text/javascript1.0"
+ | "text/javascript1.1"
+ | "text/javascript1.2"
+ | "text/javascript1.3"
+ | "text/javascript1.4"
+ | "text/javascript1.5"
+ | "text/jscript"
+ | "text/livescript"
+ | "text/x-ecmascript"
+ | "text/x-javascript" => true,
+ "text/javascript" => true,
+ _ => false,
+ }
+ }
+
fn is_default_attribute_value(
&self,
namespace: Namespace,
@@ -606,47 +636,22 @@ impl Minifier<'_> {
}
_ => {}
},
- js_word!("script") => {
- match attribute.name {
- js_word!("type") => {
- let value = if let Some(next) = attribute_value.split(';').next() {
- next
- } else {
- attribute_value
- };
-
- match value {
- // Legacy JavaScript MIME types
- "application/javascript"
- | "application/ecmascript"
- | "application/x-ecmascript"
- | "application/x-javascript"
- | "text/ecmascript"
- | "text/javascript1.0"
- | "text/javascript1.1"
- | "text/javascript1.2"
- | "text/javascript1.3"
- | "text/javascript1.4"
- | "text/javascript1.5"
- | "text/jscript"
- | "text/livescript"
- | "text/x-ecmascript"
- | "text/x-javascript" => return true,
- "text/javascript" => return true,
- _ => {}
- }
+ js_word!("script") => match attribute.name {
+ js_word!("type") => {
+ if self.is_type_text_javascript(attribute_value) {
+ return true;
}
- js_word!("language") => {
- match &*attribute_value.trim().to_ascii_lowercase() {
- "javascript" | "javascript1.2" | "javascript1.3"
- | "javascript1.4" | "javascript1.5" | "javascript1.6"
- | "javascript1.7" => return true,
- _ => {}
- }
+ }
+ js_word!("language") => {
+ match &*attribute_value.trim().to_ascii_lowercase() {
+ "javascript" | "javascript1.2" | "javascript1.3"
+ | "javascript1.4" | "javascript1.5" | "javascript1.6"
+ | "javascript1.7" => return true,
+ _ => {}
}
- _ => {}
}
- }
+ _ => {}
+ },
js_word!("link") => {
if attribute.name == js_word!("type")
&& &*attribute_value.trim().to_ascii_lowercase() == "text/css"
@@ -1336,11 +1341,16 @@ impl Minifier<'_> {
fn allow_elements_to_merge(&self, left: Option<&Child>, right: &Element) -> bool {
if let Some(Child::Element(left)) = left {
- if matches!(left.namespace, Namespace::HTML | Namespace::SVG)
+ let is_style_tag = matches!(left.namespace, Namespace::HTML | Namespace::SVG)
&& left.tag_name == js_word!("style")
&& matches!(right.namespace, Namespace::HTML | Namespace::SVG)
- && right.tag_name == js_word!("style")
- {
+ && right.tag_name == js_word!("style");
+ let is_script_tag = matches!(left.namespace, Namespace::HTML | Namespace::SVG)
+ && left.tag_name == js_word!("script")
+ && matches!(right.namespace, Namespace::HTML | Namespace::SVG)
+ && right.tag_name == js_word!("script");
+
+ if is_style_tag || is_script_tag {
let mut need_skip = false;
let mut left_attributes = left
@@ -1348,9 +1358,16 @@ impl Minifier<'_> {
.clone()
.into_iter()
.filter(|attribute| match attribute.name {
+ js_word!("src") if is_script_tag => {
+ need_skip = true;
+
+ true
+ }
js_word!("type") => {
if let Some(value) = &attribute.value {
- if value.trim().to_ascii_lowercase() == "text/css" {
+ if (is_style_tag && value.trim().to_ascii_lowercase() == "text/css")
+ || is_script_tag && self.is_type_text_javascript(value)
+ {
false
} else {
need_skip = true;
@@ -1374,9 +1391,16 @@ impl Minifier<'_> {
.clone()
.into_iter()
.filter(|attribute| match attribute.name {
+ js_word!("src") if is_script_tag => {
+ need_skip = true;
+
+ true
+ }
js_word!("type") => {
if let Some(value) = &attribute.value {
- if value.trim().to_ascii_lowercase() == "text/css" {
+ if (is_style_tag && value.trim().to_ascii_lowercase() == "text/css")
+ || (is_script_tag && self.is_type_text_javascript(value))
+ {
false
} else {
need_skip = true;
@@ -1406,18 +1430,31 @@ impl Minifier<'_> {
}
fn merge_text_children(&self, left: &Element, right: &Element) -> Vec {
+ let is_script_tag = matches!(left.namespace, Namespace::HTML | Namespace::SVG)
+ && left.tag_name == js_word!("script")
+ && matches!(right.namespace, Namespace::HTML | Namespace::SVG)
+ && right.tag_name == js_word!("script");
+
let data = left.children.iter().chain(right.children.iter()).fold(
String::new(),
|mut acc, child| match child {
- Child::Text(text) => {
+ Child::Text(text) if text.data.len() > 0 => {
acc.push_str(&text.data);
+ if is_script_tag {
+ acc.push(';');
+ }
+
acc
}
_ => acc,
},
);
+ if data.is_empty() {
+ return vec![];
+ }
+
vec![Child::Text(Text {
span: DUMMY_SP,
data: data.into(),
@@ -2004,7 +2041,9 @@ impl Minifier<'_> {
}
let minified = match String::from_utf8(buf) {
- Ok(minified) => minified,
+ // Avoid generating the sequence " minified.replace("", "<\\/script>"),
_ => return None,
};
@@ -2751,19 +2790,14 @@ impl VisitMut for Minifier<'_> {
Some(js_word!("module")) if self.need_minify_js() => {
text_type = Some(MinifierType::JsModule);
}
- Some(
- js_word!("text/javascript")
- | js_word!("text/ecmascript")
- | js_word!("text/jscript")
- | js_word!("application/javascript")
- | js_word!("application/x-javascript")
- | js_word!("application/ecmascript"),
- )
- | None
- if self.need_minify_js() =>
+ Some(value)
+ if self.need_minify_js() && self.is_type_text_javascript(&value) =>
{
text_type = Some(MinifierType::JsScript);
}
+ None if self.need_minify_js() => {
+ text_type = Some(MinifierType::JsScript);
+ }
Some(
js_word!("application/json")
| js_word!("application/ld+json")
diff --git a/crates/swc_html_minifier/tests/fixture/attribute/script-type/output.min.html b/crates/swc_html_minifier/tests/fixture/attribute/script-type/output.min.html
index 84f7e2edb7cf..f7c39847dad9 100644
--- a/crates/swc_html_minifier/tests/fixture/attribute/script-type/output.min.html
+++ b/crates/swc_html_minifier/tests/fixture/attribute/script-type/output.min.html
@@ -1,6 +1,6 @@
-')
\ No newline at end of file
+
\ No newline at end of file
diff --git a/crates/swc_html_minifier/tests/fixture/comment/basic/output.min.html b/crates/swc_html_minifier/tests/fixture/comment/basic/output.min.html
index aacda5e06504..dc2cd19adc9b 100644
--- a/crates/swc_html_minifier/tests/fixture/comment/basic/output.min.html
+++ b/crates/swc_html_minifier/tests/fixture/comment/basic/output.min.html
@@ -8,7 +8,7 @@
baz
-
+
diff --git a/crates/swc_html_minifier/tests/fixture/comment/disable-remove-comment/output.min.html b/crates/swc_html_minifier/tests/fixture/comment/disable-remove-comment/output.min.html
index a23949378ebd..3b39ee93cb82 100644
--- a/crates/swc_html_minifier/tests/fixture/comment/disable-remove-comment/output.min.html
+++ b/crates/swc_html_minifier/tests/fixture/comment/disable-remove-comment/output.min.html
@@ -10,7 +10,7 @@
baz
-
+
diff --git a/crates/swc_html_minifier/tests/fixture/comment/preserve-comments/output.min.html b/crates/swc_html_minifier/tests/fixture/comment/preserve-comments/output.min.html
index a2a73d5bc4e0..7fbf72c518aa 100644
--- a/crates/swc_html_minifier/tests/fixture/comment/preserve-comments/output.min.html
+++ b/crates/swc_html_minifier/tests/fixture/comment/preserve-comments/output.min.html
@@ -2,7 +2,7 @@
baz
-
+
test
\ No newline at end of file
diff --git a/crates/swc_html_minifier/tests/fixture/element/script-group/input.html b/crates/swc_html_minifier/tests/fixture/element/script-group/input.html
new file mode 100644
index 000000000000..218581957bae
--- /dev/null
+++ b/crates/swc_html_minifier/tests/fixture/element/script-group/input.html
@@ -0,0 +1,73 @@
+
+
+
+ Document
+
+
+breaker
+
+
+
+
+breaker
+
+
+
+
+breaker
+
+
+
+
+breaker
+
+
+
+
+breaker
+
+
+
+
+breaker
+
+
+
+
+breaker
+
+
+
+
+breaker
+
+
+
+
+breaker
+
+
+
+
+breaker
+
+
+
+
+breaker
+
+
+
+breaker
+
+
+
+
\ No newline at end of file
diff --git a/crates/swc_html_minifier/tests/fixture/element/script-group/output.min.html b/crates/swc_html_minifier/tests/fixture/element/script-group/output.min.html
new file mode 100644
index 000000000000..5c54be64d0c7
--- /dev/null
+++ b/crates/swc_html_minifier/tests/fixture/element/script-group/output.min.html
@@ -0,0 +1,44 @@
+Documentbreaker
+
+
+
+breaker
+
+
+
+breaker
+
+
+
+breaker
+
+
+
+breaker
+
+
+
+breaker
+
+
+
+breaker
+
+
+
+breaker
+
+
+
+breaker
+
+
+
+breaker
+
+
+
+breaker
+
+
+breaker
\ No newline at end of file
diff --git a/crates/swc_html_minifier/tests/fixture/element/script/input.html b/crates/swc_html_minifier/tests/fixture/element/script/input.html
index 1a19730a23c5..85d4becb9c3e 100644
--- a/crates/swc_html_minifier/tests/fixture/element/script/input.html
+++ b/crates/swc_html_minifier/tests/fixture/element/script/input.html
@@ -183,5 +183,8 @@ Party coffee cake recipe
+
+ test
+