diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e48768..f3321d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # minify-html changelog +## 0.10.1 + +- Bump [minify-js](https://github.com/wilsonzlin/minify-js) to 0.2. +- Minify JS as module instead of global script if `type` is `module`. + ## 0.10.0 - Drop unmatched closing tags instead of reinterpreting them as opening tags. This avoids the possibility of unintentionally creating a large deep tree due to malformed inputs where there are repeated unmatched closing tags (e.g. broken HTML template). diff --git a/rust/common/spec/script.rs b/rust/common/spec/script.rs index 89deb58..c51c574 100644 --- a/rust/common/spec/script.rs +++ b/rust/common/spec/script.rs @@ -8,7 +8,6 @@ lazy_static! { s.insert(b"application/javascript"); s.insert(b"application/x-ecmascript"); s.insert(b"application/x-javascript"); - s.insert(b"module"); s.insert(b"text/ecmascript"); s.insert(b"text/javascript"); s.insert(b"text/javascript1.0"); diff --git a/rust/main/Cargo.toml b/rust/main/Cargo.toml index 8b13a63..e85707b 100644 --- a/rust/main/Cargo.toml +++ b/rust/main/Cargo.toml @@ -18,6 +18,6 @@ maintenance = { status = "actively-developed" } [dependencies] aho-corasick = "0.7" css-minify = "0.2.2" -minify-js = "0.1.1" +minify-js = "0.2" lazy_static = "1.4" memchr = "2" diff --git a/rust/main/src/ast/mod.rs b/rust/main/src/ast/mod.rs index 7e059d7..225a62b 100644 --- a/rust/main/src/ast/mod.rs +++ b/rust/main/src/ast/mod.rs @@ -19,6 +19,7 @@ pub enum ScriptOrStyleLang { CSS, Data, JS, + JSModule, } pub struct AttrVal { diff --git a/rust/main/src/minify/attr.rs b/rust/main/src/minify/attr.rs index dc21516..3aabf6c 100644 --- a/rust/main/src/minify/attr.rs +++ b/rust/main/src/minify/attr.rs @@ -340,8 +340,7 @@ pub fn minify_attr( || default_value.filter(|dv| dv == &value_raw).is_some() || (tag == b"script" && name == b"type" - && JAVASCRIPT_MIME_TYPES.contains(value_raw.as_slice()) - && value_raw.as_slice() != b"module") + && JAVASCRIPT_MIME_TYPES.contains(value_raw.as_slice())) { return AttrMinified::Redundant; }; diff --git a/rust/main/src/minify/content.rs b/rust/main/src/minify/content.rs index f76d4e9..6539063 100644 --- a/rust/main/src/minify/content.rs +++ b/rust/main/src/minify/content.rs @@ -145,7 +145,12 @@ pub fn minify_content( NodeData::ScriptOrStyleContent { code, lang } => match lang { ScriptOrStyleLang::CSS => minify_css(cfg, out, &code), ScriptOrStyleLang::Data => out.extend_from_slice(&code), - ScriptOrStyleLang::JS => minify_js(cfg, out, &code), + ScriptOrStyleLang::JS => { + minify_js(cfg, minify_js::TopLevelMode::Global, out, &code) + } + ScriptOrStyleLang::JSModule => { + minify_js(cfg, minify_js::TopLevelMode::Module, out, &code) + } }, NodeData::Text { value } => out .extend_from_slice(&CHEVRON_REPLACER.replace_all(&encode_entities(&value, false))), diff --git a/rust/main/src/minify/js.rs b/rust/main/src/minify/js.rs index 2e6ac5b..6f854ce 100644 --- a/rust/main/src/minify/js.rs +++ b/rust/main/src/minify/js.rs @@ -1,13 +1,13 @@ use crate::cfg::Cfg; use crate::common::whitespace::trimmed; -use minify_js::minify as minifier; +use minify_js::{minify as minifier, TopLevelMode}; -pub fn minify_js(cfg: &Cfg, out: &mut Vec, code: &[u8]) { +pub fn minify_js(cfg: &Cfg, mode: TopLevelMode, out: &mut Vec, code: &[u8]) { if cfg.minify_js { let source = code.to_vec(); // TODO Write to `out` directly, but only if we can guarantee that the length will never exceed the input. let mut output = Vec::new(); - let result = minifier(source, &mut output); + let result = minifier(mode, source, &mut output); // TODO Collect error as warning. if !result.is_err() && output.len() < code.len() { out.extend_from_slice(output.as_slice()); diff --git a/rust/main/src/parse/element.rs b/rust/main/src/parse/element.rs index 84e3e13..738d5b5 100644 --- a/rust/main/src/parse/element.rs +++ b/rust/main/src/parse/element.rs @@ -169,6 +169,9 @@ pub fn parse_element(code: &mut Code, ns: Namespace, parent: &[u8]) -> NodeData Some(mime) if !JAVASCRIPT_MIME_TYPES.contains(mime.as_slice()) => { parse_script_content(code, ScriptOrStyleLang::Data) } + Some(typ) if typ.as_slice() == b"module" => { + parse_script_content(code, ScriptOrStyleLang::JSModule) + } _ => parse_script_content(code, ScriptOrStyleLang::JS), }, b"style" => parse_style_content(code), diff --git a/rust/onepass/Cargo.toml b/rust/onepass/Cargo.toml index a15554a..f8ecb43 100644 --- a/rust/onepass/Cargo.toml +++ b/rust/onepass/Cargo.toml @@ -20,4 +20,4 @@ aho-corasick = "0.7" css-minify = "0.2.2" lazy_static = "1.4" memchr = "2" -minify-js = "0.1.1" +minify-js = "0.2" diff --git a/rust/onepass/src/unit/script.rs b/rust/onepass/src/unit/script.rs index dde1773..a950949 100644 --- a/rust/onepass/src/unit/script.rs +++ b/rust/onepass/src/unit/script.rs @@ -14,17 +14,22 @@ lazy_static! { .build(&[" ProcessingResult<()> { +pub fn process_script( + proc: &mut Processor, + cfg: &Cfg, + mode: Option, +) -> ProcessingResult<()> { let start = WriteCheckpoint::new(proc); proc.require_not_at_end()?; let src = proc.m(WhileNotSeq(&SCRIPT_END), Discard); // `process_tag` will require closing tag. - if js && cfg.minify_js { + if cfg.minify_js && mode.is_some() { // TODO Write to `out` directly, but only if we can guarantee that the length will never exceed the input. let mut output = Vec::new(); - let result = minify_js::minify(proc[src].to_vec(), &mut output); + let result = minify_js::minify(mode.unwrap(), proc[src].to_vec(), &mut output); // TODO Collect error as warning. if !result.is_err() && output.len() < src.len() { proc.write_slice(output.as_slice()); diff --git a/rust/onepass/src/unit/tag.rs b/rust/onepass/src/unit/tag.rs index 54c0406..b0d8e1e 100644 --- a/rust/onepass/src/unit/tag.rs +++ b/rust/onepass/src/unit/tag.rs @@ -19,6 +19,7 @@ use crate::unit::style::process_style; #[derive(Copy, Clone)] enum TagType { ScriptJs, + ScriptJsModule, ScriptData, Style, Other, @@ -138,14 +139,14 @@ pub fn process_tag( match (tag_type, &proc[name]) { // NOTE: We don't support multiple `type` attributes, so can't go from ScriptData => ScriptJs. (TagType::ScriptJs, b"type") => { - // It's JS if the value is empty or one of `JAVASCRIPT_MIME_TYPES`. - let script_tag_type_is_js = value + if value.filter(|v| &proc[*v] == b"module").is_some() { + tag_type = TagType::ScriptJsModule; + } else if value .filter(|v| !JAVASCRIPT_MIME_TYPES.contains(&proc[*v])) - .is_none(); - if script_tag_type_is_js { - if &proc[value.unwrap()] != b"module" { - erase_attr = true; - }; + .is_none() + { + // The value is empty or one of `JAVASCRIPT_MIME_TYPES`. + erase_attr = true; } else { // Tag does not contain JS, don't minify JS. tag_type = TagType::ScriptData; @@ -205,8 +206,11 @@ pub fn process_tag( let mut closing_tag_omitted = false; match tag_type { - TagType::ScriptData => process_script(proc, cfg, false)?, - TagType::ScriptJs => process_script(proc, cfg, true)?, + TagType::ScriptData => process_script(proc, cfg, None)?, + TagType::ScriptJs => process_script(proc, cfg, Some(minify_js::TopLevelMode::Global))?, + TagType::ScriptJsModule => { + process_script(proc, cfg, Some(minify_js::TopLevelMode::Module))? + } TagType::Style => process_style(proc, cfg)?, _ => { closing_tag_omitted =