use napi::{
Either,
bindgen_prelude::{FromNapiValue, TypeName, ValidateNapiValue},
};
use napi_derive::napi;
use rspack_core::{
CacheOptions, CompilerOptions, Context, Experiments, ModuleOptions, NodeDirnameOption,
NodeFilenameOption, NodeGlobalOption, NodeOption, OutputOptions, References,
incremental::IncrementalPasses,
};
use rspack_error::error;
mod raw_builtins;
mod raw_cache;
mod raw_devtool;
mod raw_dynamic_entry;
mod raw_experiments;
mod raw_external;
mod raw_mode;
mod raw_module;
mod raw_node;
mod raw_optimization;
mod raw_output;
mod raw_split_chunks;
mod raw_stats;
pub use raw_builtins::*;
pub use raw_cache::*;
pub use raw_devtool::*;
pub use raw_dynamic_entry::*;
pub use raw_experiments::*;
pub use raw_external::*;
pub use raw_mode::*;
pub use raw_module::*;
pub use raw_node::*;
pub use raw_optimization::*;
pub use raw_output::*;
pub use raw_split_chunks::*;
pub use raw_stats::*;
pub use crate::options::raw_resolve::*;
use crate::virtual_modules::JsVirtualFile;
#[derive(Debug)]
#[napi(object, object_to_js = false)]
pub struct RawOptions {
pub name: Option<String>,
#[napi(ts_type = "undefined | 'production' | 'development' | 'none'")]
pub mode: Option<RawMode>,
pub context: String,
pub output: RawOutputOptions,
pub resolve: RawResolveOptions,
pub resolve_loader: RawResolveOptions,
pub module: RawModuleOptions,
pub optimization: RawOptimizationOptions,
pub stats: RawStatsOptions,
pub cache: RawCacheOptions,
pub experiments: RawExperiments,
pub node: Option<RawNodeOption>,
pub profile: bool,
pub amd: Option<String>,
pub bail: bool,
#[napi(js_name = "__references", ts_type = "Record<string, any>")]
pub __references: References,
#[napi(js_name = "__virtual_files")]
pub __virtual_files: Option<Vec<JsVirtualFile>>,
}
impl TryFrom<RawOptions> for CompilerOptions {
type Error = rspack_error::Error;
fn try_from(value: RawOptions) -> Result<Self, rspack_error::Error> {
let context: Context = value.context.into();
let output: OutputOptions = value.output.try_into()?;
let resolve = value.resolve.try_into()?;
let resolve_loader = value.resolve_loader.try_into()?;
let mode = value.mode.unwrap_or_default().into();
let module: ModuleOptions = value.module.try_into()?;
let cache = value.cache.into();
let mut experiments: Experiments = value.experiments.into();
if let CacheOptions::Disabled = cache {
experiments.incremental.passes = IncrementalPasses::empty();
}
let optimization = value.optimization.try_into()?;
let stats = value.stats.into();
let node = value
.node
.map(|n| {
let dirname = match n.dirname.as_str() {
"mock" => NodeDirnameOption::Mock,
"warn-mock" => NodeDirnameOption::WarnMock,
"eval-only" => NodeDirnameOption::EvalOnly,
"node-module" => NodeDirnameOption::NodeModule,
"true" => NodeDirnameOption::True,
"false" => NodeDirnameOption::False,
_ => return Err(error!("invalid node.dirname: {}", n.dirname.as_str())),
};
let filename = match n.filename.as_str() {
"mock" => NodeFilenameOption::Mock,
"warn-mock" => NodeFilenameOption::WarnMock,
"eval-only" => NodeFilenameOption::EvalOnly,
"node-module" => NodeFilenameOption::NodeModule,
"true" => NodeFilenameOption::True,
"false" => NodeFilenameOption::False,
_ => return Err(error!("invalid node.filename: {}", n.filename.as_str())),
};
let global = match n.global.as_str() {
"true" => NodeGlobalOption::True,
"warn" => NodeGlobalOption::Warn,
"false" => NodeGlobalOption::False,
_ => return Err(error!("invalid node.global: {}", n.global.as_str())),
};
Ok(NodeOption {
dirname,
filename,
global,
})
})
.transpose()?;
Ok(CompilerOptions {
name: value.name,
context,
mode,
module,
output,
resolve,
resolve_loader,
experiments,
stats,
cache,
optimization,
node,
profile: value.profile,
amd: value.amd,
bail: value.bail,
__references: value.__references,
})
}
}
#[derive(Debug)]
pub enum WithFalse<T> {
False,
True(T),
}
impl<T: ValidateNapiValue + FromNapiValue> FromNapiValue for WithFalse<T> {
unsafe fn from_napi_value(
env: napi::sys::napi_env,
napi_val: napi::sys::napi_value,
) -> napi::Result<Self> {
unsafe {
Either::from_napi_value(env, napi_val).map(|either| match either {
Either::A(false) => WithFalse::False,
Either::A(true) => panic!("true is not a valid value for `WithFalse`"),
Either::B(value) => WithFalse::True(value),
})
}
}
}
impl<T: ValidateNapiValue> ValidateNapiValue for WithFalse<T> {}
impl<T: TypeName> TypeName for WithFalse<T> {
fn type_name() -> &'static str {
T::type_name()
}
fn value_type() -> napi::ValueType {
T::value_type()
}
}
#[derive(Default, Debug)]
pub enum WithBool<T> {
True,
#[default]
False,
Value(T),
}
impl<T> WithBool<T> {
pub fn as_bool(&self) -> Option<bool> {
match self {
WithBool::True => Some(true),
WithBool::False => Some(false),
WithBool::Value(_) => None,
}
}
pub fn as_value(&self) -> Option<&T> {
match self {
WithBool::Value(value) => Some(value),
_ => None,
}
}
}
impl<T: ValidateNapiValue + FromNapiValue> FromNapiValue for WithBool<T> {
unsafe fn from_napi_value(
env: napi::sys::napi_env,
napi_val: napi::sys::napi_value,
) -> napi::Result<Self> {
unsafe {
Either::from_napi_value(env, napi_val).map(|either| match either {
Either::A(false) => WithBool::False,
Either::A(true) => WithBool::True,
Either::B(value) => WithBool::Value(value),
})
}
}
}