use anyhow::{Context, Result}; /// Conservative JavaScript minification without token rewriting. /// /// JavaScript `/` is context-sensitive (division vs. RegExp literal), so this /// minifier never guesses. Outside strings/templates/comments it collapses /// horizontal whitespace only until the first slash on a physical line; the /// remainder of that line is copied verbatim. This still removes common /// indentation/alignment whitespace while preserving RegExp bodies, ASI-sensitive /// line terminators and comment text exactly. pub fn minify(body: &[u8]) -> Result> { let input = std::str::from_utf8(body).context("JavaScript is not UTF-8")?; let mut out = String::with_capacity(input.len()); let mut chars = input.chars().peekable(); let mut quote: Option = None; let mut block_comment = false; let mut line_comment = false; let mut slash_sensitive = false; let mut horizontal_space = false; while let Some(ch) = chars.next() { if line_comment { out.push(ch); if ch == '\n' || ch == '\r' { line_comment = false; slash_sensitive = false; horizontal_space = false; } continue; } if block_comment { out.push(ch); if ch == '*' && chars.peek() == Some(&'/') { chars.next(); out.push('/'); block_comment = false; } if ch == '\n' || ch == '\r' { slash_sensitive = false; } continue; } if let Some(active) = quote { out.push(ch); if ch == '\\' { if let Some(next) = chars.next() { out.push(next); } } else if ch == active { quote = None; } continue; } if slash_sensitive { out.push(ch); if ch == '\n' || ch == '\r' { slash_sensitive = false; horizontal_space = false; } continue; } if matches!(ch, '\'' | '"' | '`') { if horizontal_space { out.push(' '); horizontal_space = false; } quote = Some(ch); out.push(ch); continue; } if ch == '/' { if horizontal_space { out.push(' '); horizontal_space = false; } out.push(ch); match chars.peek() { Some(&'/') => { chars.next(); out.push('/'); line_comment = true; } Some(&'*') => { chars.next(); out.push('*'); block_comment = true; } _ => slash_sensitive = true, } continue; } if matches!(ch, ' ' | '\t' | '\u{000b}' | '\u{000c}') { horizontal_space = true; continue; } if ch == '\r' || ch == '\n' { horizontal_space = false; slash_sensitive = false; out.push(ch); continue; } if horizontal_space { out.push(' '); horizontal_space = false; } out.push(ch); } let trimmed_len = out.trim_end_matches([' ', '\t']).len(); out.truncate(trimmed_len); Ok(out.into_bytes()) } #[cfg(test)] include!("js/tests.rs");