From 41093b022d8d3304f9d235fd5b9dd386fecb8c3c Mon Sep 17 00:00:00 2001 From: Alexander Akait <4567934+alexander-akait@users.noreply.github.com> Date: Tue, 18 Oct 2022 16:40:59 +0300 Subject: [PATCH] feat(html/minifier): Merge identical metadata elements (#6183) --- crates/swc_html_minifier/src/lib.rs | 51 ++++++++++++- crates/swc_html_minifier/src/option.rs | 4 + .../attribute/custom-attribute/input.html | 2 + .../custom-attribute/output.min.html | 2 + .../tests/fixture/attribute/srcdoc/input.html | 9 +++ .../fixture/attribute/srcdoc/output.min.html | 1 + .../tests/fixture/element/custom/input.html | 1 + .../fixture/element/custom/output.min.html | 3 +- .../fixture/element/style-group-1/config.json | 3 + .../fixture/element/style-group-1/input.html | 64 ++++++++++++++++ .../element/style-group-1/output.min.html | 4 + .../fixture/element/style-group/input.html | 76 +++++++++++++++++++ .../element/style-group/output.min.html | 8 ++ .../fixture/element/style/output.min.html | 2 +- .../output.min.html | 2 +- .../output.min.html | 2 +- .../collapse-whitespace-smart/output.min.html | 2 +- 17 files changed, 227 insertions(+), 9 deletions(-) create mode 100644 crates/swc_html_minifier/tests/fixture/attribute/srcdoc/input.html create mode 100644 crates/swc_html_minifier/tests/fixture/attribute/srcdoc/output.min.html create mode 100644 crates/swc_html_minifier/tests/fixture/element/style-group-1/config.json create mode 100644 crates/swc_html_minifier/tests/fixture/element/style-group-1/input.html create mode 100644 crates/swc_html_minifier/tests/fixture/element/style-group-1/output.min.html create mode 100644 crates/swc_html_minifier/tests/fixture/element/style-group/input.html create mode 100644 crates/swc_html_minifier/tests/fixture/element/style-group/output.min.html diff --git a/crates/swc_html_minifier/src/lib.rs b/crates/swc_html_minifier/src/lib.rs index b6f743da2085..563aad1aa493 100644 --- a/crates/swc_html_minifier/src/lib.rs +++ b/crates/swc_html_minifier/src/lib.rs @@ -8,8 +8,8 @@ use serde_json::Value; use swc_atoms::{js_word, JsWord}; use swc_cached::regex::CachedRegex; use swc_common::{ - collections::AHashMap, comments::SingleThreadedComments, sync::Lrc, FileName, FilePathMapping, - Mark, SourceMap, DUMMY_SP, + collections::AHashMap, comments::SingleThreadedComments, sync::Lrc, EqIgnoreSpan, FileName, + FilePathMapping, Mark, SourceMap, DUMMY_SP, }; use swc_html_ast::*; use swc_html_parser::parser::ParserConfig; @@ -1201,6 +1201,39 @@ impl Minifier<'_> { true } + fn allow_elements_to_merge(&self, left: Option<&Child>, right: &Element) -> bool { + if let Some(left) = left { + return matches!((left, right), (Child::Element(left), right) + if 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") + && left.attributes.eq_ignore_span(&right.attributes)); + } + + false + } + + fn merge_text_children(&self, left: &Element, right: &Element) -> Vec { + let data = left.children.iter().chain(right.children.iter()).fold( + String::new(), + |mut acc, child| match child { + Child::Text(text) => { + acc.push_str(&text.data); + + acc + } + _ => acc, + }, + ); + + vec![Child::Text(Text { + span: DUMMY_SP, + data: data.into(), + raw: None, + })] + } + fn minify_children(&mut self, children: &mut Vec) -> Vec { let (namespace, tag_name) = match &self.current_element { Some(element) => (element.namespace, &element.tag_name), @@ -1212,7 +1245,7 @@ impl Minifier<'_> { let mode = self.get_whitespace_minification_for_tag(namespace, tag_name); let child_will_be_retained = - |child: &mut Child, prev_children: &Vec, next_children: &Vec| { + |child: &mut Child, prev_children: &mut Vec, next_children: &mut Vec| { match child { Child::Comment(comment) if self.options.remove_comments => { self.is_preserved_comment(&comment.data) @@ -1233,6 +1266,16 @@ impl Minifier<'_> { { false } + Child::Element(element) + if self.options.merge_metadata_elements + && self.allow_elements_to_merge(prev_children.last(), element) => + { + if let Some(Child::Element(prev)) = prev_children.last_mut() { + prev.children = self.merge_text_children(prev, element); + } + + false + } Child::Text(text) if text.data.is_empty() => false, Child::Text(text) if self.need_collapse_whitespace() @@ -1539,7 +1582,7 @@ impl Minifier<'_> { } }; - let result = child_will_be_retained(&mut child, &new_children, children); + let result = child_will_be_retained(&mut child, &mut new_children, children); if result { new_children.push(child); diff --git a/crates/swc_html_minifier/src/option.rs b/crates/swc_html_minifier/src/option.rs index 74931134b61b..c40c9c6fb017 100644 --- a/crates/swc_html_minifier/src/option.rs +++ b/crates/swc_html_minifier/src/option.rs @@ -140,6 +140,10 @@ pub struct MinifyOptions { pub remove_redundant_attributes: bool, #[serde(default = "true_by_default")] pub collapse_boolean_attributes: bool, + /// Merge the same metadata elements into one (for example, consecutive + /// `style` elements will be merged into one `style` element) + #[serde(default = "true_by_default")] + pub merge_metadata_elements: bool, /// Remove extra whitespace in space and comma separated attribute values /// (where it is safe) and remove `javascript:` prefix for event handler /// attributes diff --git a/crates/swc_html_minifier/tests/fixture/attribute/custom-attribute/input.html b/crates/swc_html_minifier/tests/fixture/attribute/custom-attribute/input.html index d43b283a6a11..bbc3f84f4fea 100644 --- a/crates/swc_html_minifier/tests/fixture/attribute/custom-attribute/input.html +++ b/crates/swc_html_minifier/tests/fixture/attribute/custom-attribute/input.html @@ -10,6 +10,8 @@
+
+
diff --git a/crates/swc_html_minifier/tests/fixture/attribute/custom-attribute/output.min.html b/crates/swc_html_minifier/tests/fixture/attribute/custom-attribute/output.min.html index b79c6b143454..31e213f41d74 100644 --- a/crates/swc_html_minifier/tests/fixture/attribute/custom-attribute/output.min.html +++ b/crates/swc_html_minifier/tests/fixture/attribute/custom-attribute/output.min.html @@ -1,6 +1,8 @@ Document
+
+
\ No newline at end of file diff --git a/crates/swc_html_minifier/tests/fixture/attribute/srcdoc/input.html b/crates/swc_html_minifier/tests/fixture/attribute/srcdoc/input.html new file mode 100644 index 000000000000..d5699a5cf5bf --- /dev/null +++ b/crates/swc_html_minifier/tests/fixture/attribute/srcdoc/input.html @@ -0,0 +1,9 @@ + + + + Document + + + + + \ No newline at end of file diff --git a/crates/swc_html_minifier/tests/fixture/attribute/srcdoc/output.min.html b/crates/swc_html_minifier/tests/fixture/attribute/srcdoc/output.min.html new file mode 100644 index 000000000000..cee262308af6 --- /dev/null +++ b/crates/swc_html_minifier/tests/fixture/attribute/srcdoc/output.min.html @@ -0,0 +1 @@ +Document \ No newline at end of file diff --git a/crates/swc_html_minifier/tests/fixture/element/custom/input.html b/crates/swc_html_minifier/tests/fixture/element/custom/input.html index d7b5cc76c7d8..b860e1797250 100644 --- a/crates/swc_html_minifier/tests/fixture/element/custom/input.html +++ b/crates/swc_html_minifier/tests/fixture/element/custom/input.html @@ -10,5 +10,6 @@
Hello :)
+

Click here and here

\ No newline at end of file diff --git a/crates/swc_html_minifier/tests/fixture/element/custom/output.min.html b/crates/swc_html_minifier/tests/fixture/element/custom/output.min.html index 425f07211f34..5e531192aa2d 100644 --- a/crates/swc_html_minifier/tests/fixture/element/custom/output.min.html +++ b/crates/swc_html_minifier/tests/fixture/element/custom/output.min.html @@ -3,4 +3,5 @@ [\']["]
Hello :)
- \ No newline at end of file + +

Click here and here \ No newline at end of file diff --git a/crates/swc_html_minifier/tests/fixture/element/style-group-1/config.json b/crates/swc_html_minifier/tests/fixture/element/style-group-1/config.json new file mode 100644 index 000000000000..c1343bc13e51 --- /dev/null +++ b/crates/swc_html_minifier/tests/fixture/element/style-group-1/config.json @@ -0,0 +1,3 @@ +{ + "removeEmptyMetadataElements": false +} diff --git a/crates/swc_html_minifier/tests/fixture/element/style-group-1/input.html b/crates/swc_html_minifier/tests/fixture/element/style-group-1/input.html new file mode 100644 index 000000000000..634842353c31 --- /dev/null +++ b/crates/swc_html_minifier/tests/fixture/element/style-group-1/input.html @@ -0,0 +1,64 @@ + + + + Document + + + + + + + + + + + +

test
+ + +
test
+ + + + \ No newline at end of file diff --git a/crates/swc_html_minifier/tests/fixture/element/style-group-1/output.min.html b/crates/swc_html_minifier/tests/fixture/element/style-group-1/output.min.html new file mode 100644 index 000000000000..c80d458981d0 --- /dev/null +++ b/crates/swc_html_minifier/tests/fixture/element/style-group-1/output.min.html @@ -0,0 +1,4 @@ +Document
test
+ +
test
+ \ No newline at end of file diff --git a/crates/swc_html_minifier/tests/fixture/element/style-group/input.html b/crates/swc_html_minifier/tests/fixture/element/style-group/input.html new file mode 100644 index 000000000000..104ff9737aa0 --- /dev/null +++ b/crates/swc_html_minifier/tests/fixture/element/style-group/input.html @@ -0,0 +1,76 @@ + + + + Document + + + + + + + + + + + +

Text

+
Text
+ + +
test
+ + +
test
+ + + + \ No newline at end of file diff --git a/crates/swc_html_minifier/tests/fixture/element/style-group/output.min.html b/crates/swc_html_minifier/tests/fixture/element/style-group/output.min.html new file mode 100644 index 000000000000..82a7f7fca393 --- /dev/null +++ b/crates/swc_html_minifier/tests/fixture/element/style-group/output.min.html @@ -0,0 +1,8 @@ +Document

Text

+
Text
+ +
test
+ +
test
+ + \ No newline at end of file diff --git a/crates/swc_html_minifier/tests/fixture/element/style/output.min.html b/crates/swc_html_minifier/tests/fixture/element/style/output.min.html index 9dbc891e19ed..148cc3bc5434 100644 --- a/crates/swc_html_minifier/tests/fixture/element/style/output.min.html +++ b/crates/swc_html_minifier/tests/fixture/element/style/output.min.html @@ -4,7 +4,7 @@ - + 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 ebf254fad2ec..a3f5277b54dd 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
a b
a b c d
text
text text
test
test test
\ No newline at end of file +
a c
Empty
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-only-metdata/output.min.html b/crates/swc_html_minifier/tests/fixture/text/collapse-whitespace-only-metdata/output.min.html index 4804eff2c947..2d0ee72c3bbc 100644 --- a/crates/swc_html_minifier/tests/fixture/text/collapse-whitespace-only-metdata/output.min.html +++ b/crates/swc_html_minifier/tests/fixture/text/collapse-whitespace-only-metdata/output.min.html @@ -275,7 +275,7 @@
- +
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 c8c505ffa565..2fc10a3fa3c8 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
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
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