AkurAI Build
Menu

Public repository / beddeb15df67607b460f8291beb5c20f68db988e

AkurAI-Framework / crates/assets/src/minify.rs

Latest change 9df51040bf1687e4dc8a91557f02df52e8d5cf3a - feat(assets): content-hash fingerprinting + conservative minifiers by Ólafur Búi Ólafsson

//! Conservative, correctness-first minifiers for CSS and JS.
//!
//! These trade ratio for safety. They never reorder, rename, or rewrite code;
//! they only remove bytes that are provably insignificant (comments, redundant
//! whitespace). String, `url(...)`, and template-literal contents are passed
//! through verbatim so values are never corrupted.

/// Structural CSS punctuation around which surrounding whitespace is redundant.
fn is_struct(c: char) -> bool {
    matches!(c, '{' | '}' | ':' | ';' | ',')
}

/// Is the last emitted byte a structural CSS char? (All such chars are ASCII,
/// so a trailing UTF-8 continuation byte can never be mistaken for one.)
fn last_is_struct(out: &str) -> bool {
    matches!(
        out.as_bytes().last(),
        Some(b'{' | b'}' | b':' | b';' | b',')
    )
}

/// Resolve one run of collapsed whitespace immediately before the next token.
///
/// The space is dropped at the start of output, or when either side is a
/// structural char; otherwise a single space is kept (so multi-value
/// declarations like `0 24px 60px` survive intact).
fn flush_ws(out: &mut String, pending: &mut bool, next_struct: bool) {
    if *pending {
        *pending = false;
        if !out.is_empty() && !next_struct && !last_is_struct(out) {
            out.push(' ');
        }
    }
}

/// Is `c` part of a CSS identifier (used for `url(` word-boundary detection)?
fn is_ident(c: char) -> bool {
    c.is_ascii_alphanumeric() || c == '-' || c == '_'
}

/// Does `chars[i..]` start a `url(` token at an identifier boundary?
fn at_url(chars: &[char], i: usize) -> bool {
    if i + 4 > chars.len() {
        return false;
    }
    let m = chars[i].eq_ignore_ascii_case(&'u')
        && chars[i + 1].eq_ignore_ascii_case(&'r')
        && chars[i + 2].eq_ignore_ascii_case(&'l')
        && chars[i + 3] == '(';
    m && (i == 0 || !is_ident(chars[i - 1]))
}

/// Conservatively minify CSS.
///
/// - Strips `/* … */` comments.
/// - Collapses runs of whitespace to a single space, then drops that space
///   where it is redundant (next to `{ } : ; ,` or at the edges).
/// - Drops a `;` immediately before `}` (the trailing semicolon is optional).
/// - **Never** touches the contents of string literals (`"…"`, `'…'`) or
///   `url(…)` — data URIs, shadows, and quoted values are preserved byte for
///   byte.
///
/// ```
/// use akurai_assets::minify_css;
/// assert_eq!(
///     minify_css(".box {\n  margin: 0 24px 60px;\n}"),
///     ".box{margin:0 24px 60px}"
/// );
/// ```
pub fn minify_css(input: &str) -> String {
    let chars: Vec<char> = input.chars().collect();
    let n = chars.len();
    let mut out = String::with_capacity(input.len());
    let mut pending = false;
    let mut i = 0;

    while i < n {
        let c = chars[i];

        // Comment: /* ... */  → acts as a whitespace separator.
        if c == '/' && i + 1 < n && chars[i + 1] == '*' {
            i += 2;
            while i + 1 < n && !(chars[i] == '*' && chars[i + 1] == '/') {
                i += 1;
            }
            i = (i + 2).min(n); // skip the closing */ (clamp if unterminated)
            pending = true;
            continue;
        }

        // Whitespace: collapse; decide later whether to keep one space.
        if c.is_whitespace() {
            pending = true;
            i += 1;
            continue;
        }

        // String literal: copy verbatim (respecting backslash escapes).
        if c == '"' || c == '\'' {
            flush_ws(&mut out, &mut pending, false);
            out.push(c);
            i += 1;
            while i < n {
                let d = chars[i];
                out.push(d);
                if d == '\\' && i + 1 < n {
                    out.push(chars[i + 1]);
                    i += 2;
                    continue;
                }
                i += 1;
                if d == c {
                    break;
                }
            }
            continue;
        }

        // url(...) : copy contents verbatim so data URIs aren't corrupted.
        if at_url(&chars, i) {
            flush_ws(&mut out, &mut pending, false);
            out.push_str("url(");
            i += 4;
            let mut depth = 1usize;
            while i < n && depth > 0 {
                let d = chars[i];
                if d == '"' || d == '\'' {
                    out.push(d);
                    i += 1;
                    while i < n {
                        let e = chars[i];
                        out.push(e);
                        if e == '\\' && i + 1 < n {
                            out.push(chars[i + 1]);
                            i += 2;
                            continue;
                        }
                        i += 1;
                        if e == d {
                            break;
                        }
                    }
                    continue;
                }
                if d == '(' {
                    depth += 1;
                } else if d == ')' {
                    depth -= 1;
                }
                out.push(d);
                i += 1;
            }
            continue;
        }

        // Ordinary char.
        flush_ws(&mut out, &mut pending, is_struct(c));
        if c == '}' && out.ends_with(';') {
            out.pop();
        }
        out.push(c);
        i += 1;
    }

    out
}

/// Conservatively minify JS — a deliberately minimal, semantics-preserving pass.
///
/// - Strips `// line` and `/* block */` comments.
/// - Trims trailing whitespace and drops blank lines.
/// - Does **not** reorder, rename, join lines, or collapse intra-line
///   whitespace, so Automatic Semicolon Insertion is unaffected.
/// - String (`'…'`, `"…"`) and template-literal (`` `…` ``) contents are copied
///   verbatim, so a `//` or `/* */` *inside* a string is preserved.
///
/// ## Limitations (by design — correctness over ratio)
/// - **No regex-literal awareness.** A `/.../ ` regex containing `//` or `/*`
///   could be misread as a comment. This is rare in practice; for full safety
///   run this only on code without such regex literals, or pre-minified output.
/// - **No `${…}` interpolation parsing.** A template literal is opaque up to its
///   closing backtick; a backtick nested inside `${ }` is an edge case left
///   untouched.
///
/// ```
/// use akurai_assets::minify_js;
/// assert_eq!(minify_js("let a = 1; // a comment"), "let a = 1;");
/// assert_eq!(minify_js("let s = \"http://x\"; // c"), "let s = \"http://x\";");
/// ```
pub fn minify_js(input: &str) -> String {
    let chars: Vec<char> = input.chars().collect();
    let n = chars.len();
    let mut out = String::with_capacity(input.len());
    let mut i = 0;

    while i < n {
        let c = chars[i];

        // String / template literal: copy verbatim (respecting escapes).
        if c == '"' || c == '\'' || c == '`' {
            out.push(c);
            i += 1;
            while i < n {
                let d = chars[i];
                out.push(d);
                if d == '\\' && i + 1 < n {
                    out.push(chars[i + 1]);
                    i += 2;
                    continue;
                }
                i += 1;
                if d == c {
                    break;
                }
            }
            continue;
        }

        // Line comment: drop to end of line (leave the newline).
        if c == '/' && i + 1 < n && chars[i + 1] == '/' {
            i += 2;
            while i < n && chars[i] != '\n' {
                i += 1;
            }
            continue;
        }

        // Block comment: drop through the closing */.
        if c == '/' && i + 1 < n && chars[i + 1] == '*' {
            i += 2;
            while i + 1 < n && !(chars[i] == '*' && chars[i + 1] == '/') {
                i += 1;
            }
            i = (i + 2).min(n);
            continue;
        }

        out.push(c);
        i += 1;
    }

    // Trim trailing whitespace per line and drop blank lines.
    let mut result = String::with_capacity(out.len());
    for line in out.split('\n') {
        let trimmed = line.trim_end();
        if trimmed.is_empty() {
            continue;
        }
        if !result.is_empty() {
            result.push('\n');
        }
        result.push_str(trimmed);
    }
    result
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn css_strips_comments_and_collapses_space() {
        let css = "/* header */ .box {\n  color: red;\n}";
        assert_eq!(minify_css(css), ".box{color:red}");
    }

    #[test]
    fn css_preserves_multi_value_declarations() {
        let css = ".s {\n  margin: 0 24px 60px;\n  box-shadow: 0 24px 60px rgba(0,0,0,.5);\n}";
        assert_eq!(
            minify_css(css),
            ".s{margin:0 24px 60px;box-shadow:0 24px 60px rgba(0,0,0,.5)}"
        );
    }

    #[test]
    fn css_drops_trailing_semicolon_before_brace() {
        assert_eq!(minify_css(".a{color:red ;}"), ".a{color:red}");
    }

    #[test]
    fn css_preserves_url_contents() {
        let css = ".b { background: url(data:image/png;base64,AB,C==) ; }";
        assert_eq!(
            minify_css(css),
            ".b{background:url(data:image/png;base64,AB,C==)}"
        );
    }

    #[test]
    fn css_preserves_string_contents() {
        let css = ".c::before { content: \"a;  b : c\"; }";
        assert_eq!(minify_css(css), ".c::before{content:\"a;  b : c\"}");
    }

    #[test]
    fn js_removes_line_comment() {
        assert_eq!(minify_js("let a = 1; // a comment"), "let a = 1;");
    }

    #[test]
    fn js_removes_full_comment_line_and_block() {
        let js = "// header\nconst x = 1;\n/* block\n   spanning */\nf(x);";
        assert_eq!(minify_js(js), "const x = 1;\nf(x);");
    }

    #[test]
    fn js_preserves_string_with_double_slash() {
        let js = "const u = \"http://example.com\"; // real comment";
        let out = minify_js(js);
        assert_eq!(out, "const u = \"http://example.com\";");
        assert!(out.contains("//example.com"));
    }

    #[test]
    fn js_preserves_block_comment_marker_in_string() {
        let js = "const g = '/* not a comment */';";
        assert_eq!(minify_js(js), "const g = '/* not a comment */';");
    }

    #[test]
    fn js_collapses_blank_lines() {
        assert_eq!(minify_js("a();\n\n\n   \nb();"), "a();\nb();");
    }
}