use napi::Either;
use napi_derive::napi;
use oxc_compat::EngineTargets;
#[napi(object)]
pub struct TreeShakeOptions {
pub annotations: Option<bool>,
pub manual_pure_functions: Option<Vec<String>>,
#[napi(ts_type = "boolean | 'always'")]
pub property_read_side_effects: Option<Either<bool, String>>,
pub unknown_global_side_effects: Option<bool>,
pub invalid_import_side_effects: Option<bool>,
}
impl TryFrom<&TreeShakeOptions> for oxc_minifier::TreeShakeOptions {
type Error = String;
fn try_from(o: &TreeShakeOptions) -> Result<Self, Self::Error> {
let default = oxc_minifier::TreeShakeOptions::default();
Ok(oxc_minifier::TreeShakeOptions {
annotations: o.annotations.unwrap_or(default.annotations),
manual_pure_functions: o
.manual_pure_functions
.clone()
.unwrap_or(default.manual_pure_functions),
property_read_side_effects: match &o.property_read_side_effects {
Some(Either::A(false)) => oxc_minifier::PropertyReadSideEffects::None,
Some(Either::A(true)) => oxc_minifier::PropertyReadSideEffects::All,
Some(Either::B(s)) if s == "always" => oxc_minifier::PropertyReadSideEffects::All,
Some(Either::B(s)) => {
return Err(format!(
"Invalid propertyReadSideEffects value: '{s}'. Expected 'always'."
));
}
None => default.property_read_side_effects,
},
unknown_global_side_effects: o
.unknown_global_side_effects
.unwrap_or(default.unknown_global_side_effects),
invalid_import_side_effects: o
.invalid_import_side_effects
.unwrap_or(default.invalid_import_side_effects),
})
}
}
#[napi(object)]
pub struct CompressOptions {
pub target: Option<Either<String, Vec<String>>>,
pub drop_console: Option<bool>,
pub drop_debugger: Option<bool>,
#[napi(ts_type = "boolean | 'keep_assign'")]
pub unused: Option<Either<bool, String>>,
pub keep_names: Option<CompressOptionsKeepNames>,
pub join_vars: Option<bool>,
pub sequences: Option<bool>,
pub drop_labels: Option<Vec<String>>,
pub max_iterations: Option<u8>,
pub treeshake: Option<TreeShakeOptions>,
}
impl TryFrom<&CompressOptions> for oxc_minifier::CompressOptions {
type Error = String;
fn try_from(o: &CompressOptions) -> Result<Self, Self::Error> {
let default = oxc_minifier::CompressOptions::default();
Ok(oxc_minifier::CompressOptions {
target: match &o.target {
Some(Either::A(s)) => EngineTargets::from_target(s)?,
Some(Either::B(list)) => EngineTargets::from_target_list(list)?,
_ => default.target,
},
drop_console: o.drop_console.unwrap_or(default.drop_console),
drop_debugger: o.drop_debugger.unwrap_or(default.drop_debugger),
join_vars: o.join_vars.unwrap_or(true),
sequences: o.sequences.unwrap_or(true),
unused: match &o.unused {
Some(Either::A(true)) => oxc_minifier::CompressOptionsUnused::Remove,
Some(Either::A(false)) => oxc_minifier::CompressOptionsUnused::Keep,
Some(Either::B(s)) => match s.as_str() {
"keep_assign" => oxc_minifier::CompressOptionsUnused::KeepAssign,
_ => return Err(format!("Invalid unused option: `{s}`.")),
},
None => default.unused,
},
keep_names: o.keep_names.as_ref().map(Into::into).unwrap_or_default(),
treeshake: match &o.treeshake {
Some(ts) => oxc_minifier::TreeShakeOptions::try_from(ts)?,
None => oxc_minifier::TreeShakeOptions::default(),
},
drop_labels: o
.drop_labels
.as_ref()
.map(|labels| labels.iter().cloned().collect())
.unwrap_or_default(),
max_iterations: o.max_iterations,
})
}
}
#[napi(object)]
pub struct CompressOptionsKeepNames {
pub function: bool,
pub class: bool,
}
impl From<&CompressOptionsKeepNames> for oxc_minifier::CompressOptionsKeepNames {
fn from(o: &CompressOptionsKeepNames) -> Self {
oxc_minifier::CompressOptionsKeepNames { function: o.function, class: o.class }
}
}
#[napi(object)]
#[derive(Default)]
pub struct MangleOptions {
pub toplevel: Option<bool>,
pub keep_names: Option<Either<bool, MangleOptionsKeepNames>>,
pub debug: Option<bool>,
}
impl From<&MangleOptions> for oxc_minifier::MangleOptions {
fn from(o: &MangleOptions) -> Self {
let default = oxc_minifier::MangleOptions::default();
Self {
top_level: o.toplevel,
keep_names: match &o.keep_names {
Some(Either::A(false)) => oxc_minifier::MangleOptionsKeepNames::all_false(),
Some(Either::A(true)) => oxc_minifier::MangleOptionsKeepNames::all_true(),
Some(Either::B(o)) => oxc_minifier::MangleOptionsKeepNames::from(o),
None => default.keep_names,
},
debug: o.debug.unwrap_or(default.debug),
}
}
}
#[napi(object)]
pub struct MangleOptionsKeepNames {
pub function: bool,
pub class: bool,
}
impl From<&MangleOptionsKeepNames> for oxc_minifier::MangleOptionsKeepNames {
fn from(o: &MangleOptionsKeepNames) -> Self {
oxc_minifier::MangleOptionsKeepNames { function: o.function, class: o.class }
}
}
#[napi(object)]
pub struct CodegenOptions {
pub remove_whitespace: Option<bool>,
}
impl Default for CodegenOptions {
fn default() -> Self {
Self { remove_whitespace: Some(true) }
}
}
impl From<&CodegenOptions> for oxc_codegen::CodegenOptions {
fn from(o: &CodegenOptions) -> Self {
if o.remove_whitespace.is_some_and(|b| b) {
oxc_codegen::CodegenOptions::minify()
} else {
oxc_codegen::CodegenOptions { minify: false, ..oxc_codegen::CodegenOptions::minify() }
}
}
}
#[napi(object)]
#[derive(Default)]
pub struct MinifyOptions {
pub module: Option<bool>,
pub compress: Option<Either<bool, CompressOptions>>,
pub mangle: Option<Either<bool, MangleOptions>>,
pub codegen: Option<Either<bool, CodegenOptions>>,
pub sourcemap: Option<bool>,
}
impl TryFrom<&MinifyOptions> for oxc_minifier::MinifierOptions {
type Error = String;
fn try_from(o: &MinifyOptions) -> Result<Self, Self::Error> {
let compress = match &o.compress {
Some(Either::A(false)) => None,
None | Some(Either::A(true)) => Some(oxc_minifier::CompressOptions::default()),
Some(Either::B(o)) => Some(oxc_minifier::CompressOptions::try_from(o)?),
};
let mangle = match &o.mangle {
Some(Either::A(false)) => None,
None | Some(Either::A(true)) => Some(oxc_minifier::MangleOptions::default()),
Some(Either::B(o)) => Some(oxc_minifier::MangleOptions::from(o)),
};
Ok(oxc_minifier::MinifierOptions { compress, mangle })
}
}