v0.24.5
This commit is contained in:
commit
5e23e9af02
240 files changed
+99957
No files matched your search
@@ -0,0 +1,140 @@
|
||||
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<Vec<u8>> {
|
||||
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<char> = 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)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn keeps_regex_literals_and_asi_sensitive_newlines() {
|
||||
let input = b"const r = /a b[/*]/; // keep\nreturn value;\n";
|
||||
let output = String::from_utf8(minify(input).unwrap()).unwrap();
|
||||
assert!(output.contains("/a b[/*]/; // keep"));
|
||||
assert!(output.contains("return value"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn keeps_template_literal_whitespace() {
|
||||
let input = b"const x = `a b ${ value }`;\n";
|
||||
let output = String::from_utf8(minify(input).unwrap()).unwrap();
|
||||
assert_eq!(output, "const x = `a b ${ value }`;\n");
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user