Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(html/minifier): refactor collapse_whitespaces #5070

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
42 changes: 24 additions & 18 deletions crates/swc_html_minifier/src/lib.rs
Expand Up @@ -318,7 +318,7 @@ struct Minifier {
descendant_of_pre: bool,

force_set_html5_doctype: bool,
collapse_whitespaces: Option<CollapseWhitespaces>,
collapse_whitespaces: CollapseWhitespaces,

remove_comments: bool,
preserve_comments: Option<Vec<CachedRegex>>,
Expand Down Expand Up @@ -650,6 +650,10 @@ impl Minifier {
false
}

fn need_collapse_whitespace(&self) -> bool {
!matches!(self.collapse_whitespaces, CollapseWhitespaces::None)
}

fn get_display(&self, namespace: Namespace, tag_name: &str) -> Display {
match namespace {
Namespace::HTML => {
Expand Down Expand Up @@ -836,13 +840,15 @@ impl Minifier {

fn get_whitespace_minification_for_tag(
&self,
mode: &CollapseWhitespaces,
namespace: Namespace,
tag_name: &str,
) -> WhitespaceMinificationMode {
let default_trim = match mode {
let default_trim = match self.collapse_whitespaces {
CollapseWhitespaces::All => true,
CollapseWhitespaces::Smart | CollapseWhitespaces::Conservative => false,
CollapseWhitespaces::Smart
| CollapseWhitespaces::Conservative
| CollapseWhitespaces::OnlyMetadata
| CollapseWhitespaces::None => false,
};

match namespace {
Expand Down Expand Up @@ -952,37 +958,37 @@ impl Minifier {
_ => return,
};

let mode = self
.collapse_whitespaces
.as_ref()
.map(|mode| self.get_whitespace_minification_for_tag(mode, namespace, tag_name));
let mode = self.get_whitespace_minification_for_tag(namespace, tag_name);

let child_will_be_retained =
|child: &mut Child, prev: Option<&Child>, next: Option<&Child>| {
match child {
Child::Comment(comment) if self.remove_comments => {
self.is_preserved_comment(&comment.data)
}
// Always remove whitespaces from html and head elements (except nested
// elements), it should be safe
Child::Text(text) if text.data.is_empty() => false,
Child::Text(text)
if namespace == Namespace::HTML
if self.need_collapse_whitespace()
&& namespace == Namespace::HTML
&& matches!(&**tag_name, "html" | "head")
&& text.data.chars().all(is_whitespace) =>
{
false
}
Child::Text(text) if text.data.is_empty() => false,
Child::Text(text)
if !self.descendant_of_pre
&& get_white_space(namespace, tag_name) == WhiteSpace::Normal
&& mode.is_some() =>
&& matches!(
self.collapse_whitespaces,
CollapseWhitespaces::All
| CollapseWhitespaces::Smart
| CollapseWhitespaces::Conservative
) =>
{
let mode = mode.unwrap();
let mut is_smart_left_trim = false;
let mut is_smart_right_trim = false;

if self.collapse_whitespaces == Some(CollapseWhitespaces::Smart) {
if self.collapse_whitespaces == CollapseWhitespaces::Smart {
let prev_display = if let Some(Child::Element(Element {
namespace,
tag_name,
Expand Down Expand Up @@ -1653,7 +1659,7 @@ impl VisitMut for Minifier {

self.current_element = None;

if self.collapse_whitespaces == Some(CollapseWhitespaces::Smart) {
if self.need_collapse_whitespace() {
self.latest_element = Some(n.clone());
}
}
Expand All @@ -1672,15 +1678,15 @@ impl VisitMut for Minifier {

let old_descendant_of_pre = self.descendant_of_pre;

if self.collapse_whitespaces.is_some() && !old_descendant_of_pre {
if self.need_collapse_whitespace() && !old_descendant_of_pre {
self.descendant_of_pre = get_white_space(n.namespace, &n.tag_name) == WhiteSpace::Pre;
}

self.minify_children(&mut n.children);

n.visit_mut_children_with(self);

if self.collapse_whitespaces.is_some() {
if self.need_collapse_whitespace() {
self.descendant_of_pre = old_descendant_of_pre;
}

Expand Down
31 changes: 22 additions & 9 deletions crates/swc_html_minifier/src/option.rs
Expand Up @@ -12,14 +12,32 @@ pub enum MinifierType {
Html,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
#[serde(rename_all = "kebab-case")]
pub enum CollapseWhitespaces {
/// Keep whitespaces
None,
/// Remove all whitespaces
All,
/// Remove and collapse whitespaces based on the `display` CSS property
Smart,
/// Remove and collapse multiple whitespace into one whitespace
Conservative,
/// Remove whitespace in the `head` element, trim whitespaces for the `body`
/// element, remove spaces between `metadata` elements (i.e.
/// `script`/`style`/etc)
OnlyMetadata,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[serde(deny_unknown_fields)]
pub struct MinifyOptions {
#[serde(default)]
pub force_set_html5_doctype: bool,
#[serde(default)]
pub collapse_whitespaces: Option<CollapseWhitespaces>,
#[serde(default = "default_collapse_whitespaces")]
pub collapse_whitespaces: CollapseWhitespaces,
#[serde(default = "true_by_default")]
pub remove_comments: bool,
#[serde(default = "default_preserve_comments")]
Expand Down Expand Up @@ -76,13 +94,8 @@ impl Default for MinifyOptions {
}
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
#[serde(rename_all = "kebab-case")]
pub enum CollapseWhitespaces {
All,
Smart,
Conservative,
const fn default_collapse_whitespaces() -> CollapseWhitespaces {
CollapseWhitespaces::OnlyMetadata
}

const fn true_by_default() -> bool {
Expand Down
@@ -0,0 +1,3 @@
{
"collapseWhitespaces": "none"
}