#![warn(clippy::all, clippy::pedantic, clippy::nursery)]
#![allow(clippy::module_name_repetitions, clippy::missing_errors_doc)]
pub mod config;
pub mod minify;
use anyhow::Result;
pub use config::{MinifyOptions, Target};
pub struct Minifier {
options: MinifyOptions,
}
pub struct MinifyResult {
pub code: String,
pub map: Option<String>,
pub stats: MinifyStats,
}
#[derive(Debug, Default, Clone)]
pub struct MinifyStats {
pub original_size: usize,
pub minified_size: usize,
pub compression_ratio: f64,
pub time_ms: u128,
}
impl Minifier {
#[must_use]
pub const fn new(options: MinifyOptions) -> Self {
Self { options }
}
pub fn minify_js(&self, source: &str) -> Result<MinifyResult> {
minify::js::minify(source, &self.options)
}
pub fn minify_css(&self, source: &str) -> Result<MinifyResult> {
minify::css::minify(source, &self.options)
}
pub fn minify_json(&self, source: &str) -> Result<MinifyResult> {
minify::json::minify(source, &self.options)
}
pub fn minify_auto(&self, source: &str, filename: Option<&str>) -> Result<MinifyResult> {
let format = detect_format(source, filename);
match format {
Format::JavaScript => self.minify_js(source),
Format::CSS => self.minify_css(source),
Format::JSON => self.minify_json(source),
}
}
}
#[allow(clippy::upper_case_acronyms)]
enum Format {
JavaScript,
CSS,
JSON,
}
fn detect_format(source: &str, filename: Option<&str>) -> Format {
if let Some(name) = filename
&& let Some(ext) = std::path::Path::new(name).extension() {
if ext.eq_ignore_ascii_case("css") {
return Format::CSS;
}
if ext.eq_ignore_ascii_case("json") {
return Format::JSON;
}
if ext.eq_ignore_ascii_case("js") || ext.eq_ignore_ascii_case("mjs") || ext.eq_ignore_ascii_case("cjs") {
return Format::JavaScript;
}
}
let trimmed = source.trim_start();
if trimmed.starts_with('{') || trimmed.starts_with('[') {
if serde_json::from_str::<serde_json::Value>(source).is_ok() {
return Format::JSON;
}
}
Format::JavaScript
}
impl MinifyStats {
#[must_use]
#[allow(clippy::cast_precision_loss)]
pub fn with_sizes(original_size: usize, minified_size: usize) -> Self {
let compression_ratio = if original_size > 0 {
1.0 - (minified_size as f64 / original_size as f64)
} else {
0.0
};
Self {
original_size,
minified_size,
compression_ratio,
time_ms: 0,
}
}
#[must_use]
pub const fn with_time(mut self, time_ms: u128) -> Self {
self.time_ms = time_ms;
self
}
}