From 138de76de9b3ac5eaf4c14416c7be9b266d5e2db Mon Sep 17 00:00:00 2001 From: Wilson Lin Date: Sat, 7 Aug 2021 18:59:54 +1000 Subject: [PATCH] Minify style attributes as well --- src/minify/attr.rs | 26 ++++++++++++++++++++++- src/minify/css.rs | 9 ++++++-- src/minify/element.rs | 2 +- src/minify/esbuild.rs | 48 ++++++++++++++++++++++--------------------- src/minify/js.rs | 2 +- src/tests/mod.rs | 6 ++++++ 6 files changed, 65 insertions(+), 28 deletions(-) diff --git a/src/minify/attr.rs b/src/minify/attr.rs index 14b3b63..4dc93bb 100644 --- a/src/minify/attr.rs +++ b/src/minify/attr.rs @@ -1,6 +1,11 @@ use aho_corasick::{AhoCorasickBuilder, MatchKind}; use lazy_static::lazy_static; +#[cfg(feature = "js-esbuild")] +use { + crate::minify::css::MINIFY_CSS_TRANSFORM_OPTIONS, crate::minify::esbuild::minify_using_esbuild, +}; + use crate::gen::attrs::ATTRS; use crate::gen::codepoints::DIGIT; use crate::pattern::Replacer; @@ -8,6 +13,7 @@ use crate::spec::entity::encode::encode_entities; use crate::spec::script::JAVASCRIPT_MIME_TYPES; use crate::spec::tag::ns::Namespace; use crate::whitespace::{collapse_whitespace, left_trim, right_trim}; +use crate::Cfg; fn build_double_quoted_replacer() -> Replacer { let mut patterns = Vec::>::new(); @@ -187,7 +193,13 @@ pub enum AttrMinified { Value(AttrMinifiedValue), } -pub fn minify_attr(ns: Namespace, tag: &[u8], name: &[u8], mut value_raw: Vec) -> AttrMinified { +pub fn minify_attr( + cfg: &Cfg, + ns: Namespace, + tag: &[u8], + name: &[u8], + mut value_raw: Vec, +) -> AttrMinified { let attr_cfg = ATTRS.get(ns, tag, name); let should_collapse_and_trim = attr_cfg.filter(|attr| attr.collapse_and_trim).is_some(); @@ -203,6 +215,18 @@ pub fn minify_attr(ns: Namespace, tag: &[u8], name: &[u8], mut value_raw: Vec = { + pub static ref MINIFY_CSS_TRANSFORM_OPTIONS: Arc = { let mut builder = TransformOptionsBuilder::new(); builder.loader = Loader::CSS; builder.minify_identifiers = true; @@ -34,6 +34,11 @@ pub fn minify_css(cfg: &Cfg, out: &mut Vec, code: &[u8]) { if !cfg.minify_css { out.extend_from_slice(&code); } else { - minify_using_esbuild(out, code, &TRANSFORM_OPTIONS.clone(), &STYLE_END); + minify_using_esbuild( + out, + code, + &MINIFY_CSS_TRANSFORM_OPTIONS.clone(), + Some(&STYLE_END), + ); } } diff --git a/src/minify/element.rs b/src/minify/element.rs index 3f88473..b5fe2d6 100644 --- a/src/minify/element.rs +++ b/src/minify/element.rs @@ -46,7 +46,7 @@ pub fn minify_element( let mut attrs_sorted = attributes.into_iter().collect::>(); attrs_sorted.sort_unstable_by(|a, b| a.0.cmp(&b.0)); for (name, value) in attrs_sorted { - let min = minify_attr(ns, tag_name, &name, value); + let min = minify_attr(cfg, ns, tag_name, &name, value); if let AttrMinified::Redundant = min { continue; }; diff --git a/src/minify/esbuild.rs b/src/minify/esbuild.rs index 953a034..6f7daba 100644 --- a/src/minify/esbuild.rs +++ b/src/minify/esbuild.rs @@ -3,38 +3,40 @@ use {aho_corasick::AhoCorasick, crossbeam::sync::WaitGroup, esbuild_rs::Transfor #[cfg(feature = "js-esbuild")] // TODO The use of WG is ugly and we don't want to be multi-threaded; wait for Rust port esbuild-transform-rs. -// `tag_to_escape` must be case insensitive. +// `tag_to_escape` must be case insensitive if provided. pub fn minify_using_esbuild( out: &mut Vec, code: &[u8], transform_options: &TransformOptions, - tag_to_escape: &'static AhoCorasick, + tag_to_escape: Option<&'static AhoCorasick>, ) { let wg = WaitGroup::new(); unsafe { let wg = wg.clone(); esbuild_rs::transform_direct_unmanaged(code, transform_options, move |result| { - // TODO (JS) Handle other forms: - // 1 < /script/.exec(a).length - // ` ${` ${a - // /* - // Considerations: - // - Need to parse strings (e.g. "", '', ``) so syntax within strings aren't mistakenly interpreted as code. - // - Need to be able to parse regex literals to determine string delimiters aren't actually characters in the regex. - // - Determining whether a slash is division or regex requires a full-blown JS parser to handle all cases (this is a well-known JS parsing problem). - // - `/ out.extend_from_slice(min_code), + // TODO (JS) Handle other forms: + // 1 < /script/.exec(a).length + // ` ${` ${a + // /* + // Considerations: + // - Need to parse strings (e.g. "", '', ``) so syntax within strings aren't mistakenly interpreted as code. + // - Need to be able to parse regex literals to determine string delimiters aren't actually characters in the regex. + // - Determining whether a slash is division or regex requires a full-blown JS parser to handle all cases (this is a well-known JS parsing problem). + // - `/ { + tag_to_escape.replace_all_with_bytes(min_code, out, |_, orig, dst| { + dst.extend(b"<\\/"); + // Keep original case. + dst.extend(&orig[2..]); + true + }) + } + } drop(wg); }); }; diff --git a/src/minify/js.rs b/src/minify/js.rs index 376316f..ebc0f92 100644 --- a/src/minify/js.rs +++ b/src/minify/js.rs @@ -33,6 +33,6 @@ pub fn minify_js(cfg: &Cfg, out: &mut Vec, code: &[u8]) { if !cfg.minify_js { out.extend_from_slice(&code); } else { - minify_using_esbuild(out, code, &TRANSFORM_OPTIONS.clone(), &SCRIPT_END); + minify_using_esbuild(out, code, &TRANSFORM_OPTIONS.clone(), Some(&SCRIPT_END)); } } diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 510f6b3..32107c4 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -596,8 +596,14 @@ fn test_js_minification_unintentional_closing_tag() { #[cfg(feature = "js-esbuild")] #[test] fn test_css_minification() { + // `", b"", ); + // `style` attributes. + eval_with_css_min( + br#"
"#, + br#"
"#, + ); }