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 @@ +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/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
    + diff --git a/crates/swc_html_minifier/tests/fixture/element/script/output.min.html b/crates/swc_html_minifier/tests/fixture/element/script/output.min.html index ef8ba245813d..64c51fdd794c 100644 --- a/crates/swc_html_minifier/tests/fixture/element/script/output.min.html +++ b/crates/swc_html_minifier/tests/fixture/element/script/output.min.html @@ -19,7 +19,11 @@

    Party coffee cake recipe

    +

    Party coffee cake recipe

    by Mary Stone, 2018-03-10

    @@ -54,4 +58,5 @@ alert('test') - \ No newline at end of file + +
    test
    \ No newline at end of file diff --git a/crates/swc_html_minifier/tests/fixture/text/collapse-whitespace-advanced-conservative/output.min.html b/crates/swc_html_minifier/tests/fixture/text/collapse-whitespace-advanced-conservative/output.min.html index f8218698b702..40772446a8fc 100644 --- a/crates/swc_html_minifier/tests/fixture/text/collapse-whitespace-advanced-conservative/output.min.html +++ b/crates/swc_html_minifier/tests/fixture/text/collapse-whitespace-advanced-conservative/output.min.html @@ -29,4 +29,4 @@ foo baz -
    a c
    Empty
    foo bar
    a b
    a b c d
    text
    text text
    test
    test test
    \ No newline at end of file +
    a c
    Empty
    foo bar
    a b
    a b c d
    text
    text text
    test
    test test
    \ No newline at end of file diff --git a/crates/swc_html_minifier/tests/fixture/text/collapse-whitespace-smart/output.min.html b/crates/swc_html_minifier/tests/fixture/text/collapse-whitespace-smart/output.min.html index 6a7776158647..91a99a487500 100644 --- a/crates/swc_html_minifier/tests/fixture/text/collapse-whitespace-smart/output.min.html +++ b/crates/swc_html_minifier/tests/fixture/text/collapse-whitespace-smart/output.min.html @@ -29,4 +29,4 @@ foo baz -
    a c
    Empty
    foo bar
    a b
    a b c d
    text
    text text
    test test
    test testtest
    test test
    test test
    test test
    test test
    testtest test
    test
    test test
    test
    test
    test test test test test This is bold and red This is bold and red testtestThis is bold and red This is bold and red
    test a b test test a b test test a b test
    test test
    a
    \ No newline at end of file +
    a c
    Empty
    foo bar
    a b
    a b c d
    text
    text text
    test test
    test testtest
    test test
    test test
    test test
    test test
    testtest test
    test
    test test
    test
    test
    test test test test test This is bold and red This is bold and red testtestThis is bold and red This is bold and red
    test a b test test a b test test a b test
    test test
    a
    \ No newline at end of file