use std::path::PathBuf;
#[cfg(feature = "react_compiler")]
use oxc_react_compiler::PluginOptions;
use crate::{
ReactRefreshOptions,
common::helper_loader::{HelperLoaderMode, HelperLoaderOptions},
compiler_assumptions::CompilerAssumptions,
decorator::DecoratorOptions,
es2015::ES2015Options,
es2016::ES2016Options,
es2017::ES2017Options,
es2018::ES2018Options,
es2019::ES2019Options,
es2020::ES2020Options,
es2021::ES2021Options,
es2022::ES2022Options,
es2026::ES2026Options,
jsx::JsxOptions,
plugins::{PluginsOptions, StyledComponentsOptions},
proposals::ProposalOptions,
regexp::RegExpOptions,
typescript::TypeScriptOptions,
};
pub mod babel;
mod env;
mod module;
use babel::BabelOptions;
pub use env::EnvOptions;
pub use module::Module;
pub use oxc_compat::{ESFeature, Engine, EngineTargets};
pub use oxc_syntax::es_target::ESTarget;
#[derive(Debug, Default, Clone)]
pub struct TransformOptions {
pub cwd: PathBuf,
pub assumptions: CompilerAssumptions,
#[cfg(feature = "react_compiler")]
pub react_compiler: Option<PluginOptions>,
pub typescript: TypeScriptOptions,
pub decorator: DecoratorOptions,
pub plugins: PluginsOptions,
pub jsx: JsxOptions,
pub env: EnvOptions,
pub proposals: ProposalOptions,
pub helper_loader: HelperLoaderOptions,
}
impl TransformOptions {
#[doc(hidden)]
pub fn enable_all() -> Self {
Self {
cwd: PathBuf::new(),
assumptions: CompilerAssumptions::default(),
#[cfg(feature = "react_compiler")]
react_compiler: None,
typescript: TypeScriptOptions::default(),
decorator: DecoratorOptions {
legacy: true,
emit_decorator_metadata: true,
strict_null_checks: true,
},
plugins: PluginsOptions {
styled_components: Some(StyledComponentsOptions::default()),
tagged_template_transform: true,
},
jsx: JsxOptions {
development: true,
refresh: Some(ReactRefreshOptions::default()),
..JsxOptions::default()
},
env: EnvOptions::enable_all( false),
proposals: ProposalOptions::default(),
helper_loader: HelperLoaderOptions {
mode: HelperLoaderMode::Runtime,
..Default::default()
},
}
}
pub fn from_target(s: &str) -> Result<Self, String> {
EnvOptions::from_target(s).map(|env| Self { env, ..Self::default() })
}
pub fn from_target_list<S: AsRef<str>>(list: &[S]) -> Result<Self, String> {
EnvOptions::from_target_list(list).map(|env| Self { env, ..Self::default() })
}
}
impl From<ESTarget> for TransformOptions {
fn from(target: ESTarget) -> Self {
use oxc_compat::ESVersion;
let mut engine_targets = EngineTargets::default();
engine_targets.insert(Engine::Es, target.version());
let env = EnvOptions::from(engine_targets);
Self { env, ..Self::default() }
}
}
impl TryFrom<&BabelOptions> for TransformOptions {
type Error = Vec<String>;
fn try_from(options: &BabelOptions) -> Result<Self, Self::Error> {
let mut errors = Vec::<String>::new();
errors.extend(options.plugins.errors.iter().map(Clone::clone));
errors.extend(options.presets.errors.iter().map(Clone::clone));
let typescript = options
.presets
.typescript
.clone()
.or_else(|| options.plugins.typescript.clone())
.unwrap_or_default();
let decorator = DecoratorOptions {
legacy: options.plugins.legacy_decorator.is_some(),
emit_decorator_metadata: options
.plugins
.legacy_decorator
.is_some_and(|o| o.emit_decorator_metadata),
strict_null_checks: options
.plugins
.legacy_decorator
.is_none_or(|o| o.strict_null_checks),
};
let jsx = if let Some(options) = &options.presets.jsx {
options.clone()
} else {
let mut jsx_options = if let Some(options) = &options.plugins.react_jsx_dev {
options.clone()
} else if let Some(options) = &options.plugins.react_jsx {
options.clone()
} else {
JsxOptions::default()
};
jsx_options.development = options.plugins.react_jsx_dev.is_some();
jsx_options.jsx_plugin = options.plugins.react_jsx.is_some();
jsx_options.display_name_plugin = options.plugins.react_display_name;
jsx_options.jsx_self_plugin = options.plugins.react_jsx_self;
jsx_options.jsx_source_plugin = options.plugins.react_jsx_source;
jsx_options
};
let env = options.presets.env.unwrap_or_default();
let module = Module::try_from(&options.plugins).unwrap_or_else(|_| {
options.presets.env.as_ref().map(|env| env.module).unwrap_or_default()
});
let regexp = RegExpOptions {
sticky_flag: env.regexp.sticky_flag || options.plugins.sticky_flag,
unicode_flag: env.regexp.unicode_flag || options.plugins.unicode_flag,
dot_all_flag: env.regexp.dot_all_flag || options.plugins.dot_all_flag,
look_behind_assertions: env.regexp.look_behind_assertions
|| options.plugins.look_behind_assertions,
named_capture_groups: env.regexp.named_capture_groups
|| options.plugins.named_capture_groups,
unicode_property_escapes: env.regexp.unicode_property_escapes
|| options.plugins.unicode_property_escapes,
match_indices: env.regexp.match_indices,
set_notation: env.regexp.set_notation || options.plugins.set_notation,
};
let es2015 = ES2015Options {
arrow_function: options.plugins.arrow_function.or(env.es2015.arrow_function),
};
let es2016 = ES2016Options {
exponentiation_operator: options.plugins.exponentiation_operator
|| env.es2016.exponentiation_operator,
};
let es2017 = ES2017Options {
async_to_generator: options.plugins.async_to_generator || env.es2017.async_to_generator,
};
let es2018 = ES2018Options {
object_rest_spread: options
.plugins
.object_rest_spread
.or(env.es2018.object_rest_spread),
async_generator_functions: options.plugins.async_generator_functions
|| env.es2018.async_generator_functions,
};
let es2019 = ES2019Options {
optional_catch_binding: options.plugins.optional_catch_binding
|| env.es2019.optional_catch_binding,
};
let es2020 = ES2020Options {
export_namespace_from: options.plugins.export_namespace_from
|| env.es2020.export_namespace_from,
optional_chaining: options.plugins.optional_chaining || env.es2020.optional_chaining,
nullish_coalescing_operator: options.plugins.nullish_coalescing_operator
|| env.es2020.nullish_coalescing_operator,
big_int: env.es2020.big_int,
arbitrary_module_namespace_names: env.es2020.arbitrary_module_namespace_names,
};
let es2021 = ES2021Options {
logical_assignment_operators: options.plugins.logical_assignment_operators
|| env.es2021.logical_assignment_operators,
};
let es2022 = ES2022Options {
class_static_block: options.plugins.class_static_block || env.es2022.class_static_block,
class_properties: options.plugins.class_properties.or(env.es2022.class_properties),
top_level_await: env.es2022.top_level_await,
};
if !errors.is_empty() {
return Err(errors);
}
let helper_loader = HelperLoaderOptions {
mode: if options.external_helpers {
HelperLoaderMode::External
} else {
HelperLoaderMode::default()
},
..HelperLoaderOptions::default()
};
let mut plugins = PluginsOptions::default();
if let Some(styled_components) = &options.plugins.styled_components {
plugins.styled_components = Some(styled_components.clone());
}
plugins.tagged_template_transform = options.plugins.tagged_template_escape;
Ok(Self {
cwd: options.cwd.clone().unwrap_or_default(),
assumptions: options.assumptions,
#[cfg(feature = "react_compiler")]
react_compiler: None,
typescript,
decorator,
plugins,
jsx,
env: EnvOptions {
module,
es2026: ES2026Options {
explicit_resource_management: options.plugins.explicit_resource_management,
},
es2022,
es2021,
es2020,
es2019,
es2018,
es2017,
es2016,
es2015,
regexp,
},
proposals: ProposalOptions::default(),
helper_loader,
})
}
}