1use std::path::PathBuf;
2
3use crate::{
4 ReactRefreshOptions,
5 common::helper_loader::{HelperLoaderMode, HelperLoaderOptions},
6 compiler_assumptions::CompilerAssumptions,
7 decorator::DecoratorOptions,
8 es2015::ES2015Options,
9 es2016::ES2016Options,
10 es2017::ES2017Options,
11 es2018::ES2018Options,
12 es2019::ES2019Options,
13 es2020::ES2020Options,
14 es2021::ES2021Options,
15 es2022::ES2022Options,
16 es2026::ES2026Options,
17 jsx::JsxOptions,
18 plugins::{PluginsOptions, StyledComponentsOptions},
19 proposals::ProposalOptions,
20 regexp::RegExpOptions,
21 typescript::TypeScriptOptions,
22};
23
24pub mod babel;
25mod env;
26mod module;
27
28use babel::BabelOptions;
29pub use env::EnvOptions;
30pub use module::Module;
31pub use oxc_compat::{Engine, EngineTargets};
32pub use oxc_syntax::es_target::ESTarget;
33
34#[derive(Debug, Default, Clone)]
36pub struct TransformOptions {
37 pub cwd: PathBuf,
42
43 pub assumptions: CompilerAssumptions,
47
48 pub typescript: TypeScriptOptions,
51
52 pub decorator: DecoratorOptions,
54
55 pub jsx: JsxOptions,
59
60 pub env: EnvOptions,
62
63 pub proposals: ProposalOptions,
65
66 pub plugins: PluginsOptions,
68
69 pub helper_loader: HelperLoaderOptions,
70}
71
72impl TransformOptions {
73 #[doc(hidden)]
77 pub fn enable_all() -> Self {
78 Self {
79 cwd: PathBuf::new(),
80 assumptions: CompilerAssumptions::default(),
81 typescript: TypeScriptOptions::default(),
82 decorator: DecoratorOptions { legacy: true, emit_decorator_metadata: true },
83 jsx: JsxOptions {
84 development: true,
85 refresh: Some(ReactRefreshOptions::default()),
86 ..JsxOptions::default()
87 },
88 env: EnvOptions::enable_all(false),
89 proposals: ProposalOptions::default(),
90 plugins: PluginsOptions { styled_components: Some(StyledComponentsOptions::default()) },
91 helper_loader: HelperLoaderOptions {
92 mode: HelperLoaderMode::Runtime,
93 ..Default::default()
94 },
95 }
96 }
97
98 pub fn from_target(s: &str) -> Result<Self, String> {
108 EnvOptions::from_target(s).map(|env| Self { env, ..Self::default() })
109 }
110
111 pub fn from_target_list<S: AsRef<str>>(list: &[S]) -> Result<Self, String> {
126 EnvOptions::from_target_list(list).map(|env| Self { env, ..Self::default() })
127 }
128}
129
130impl From<ESTarget> for TransformOptions {
131 fn from(target: ESTarget) -> Self {
132 use oxc_compat::ESVersion;
133 let mut engine_targets = EngineTargets::default();
134 engine_targets.insert(Engine::Es, target.version());
135 let env = EnvOptions::from(engine_targets);
136 Self { env, ..Self::default() }
137 }
138}
139
140impl TryFrom<&BabelOptions> for TransformOptions {
141 type Error = Vec<String>;
142
143 fn try_from(options: &BabelOptions) -> Result<Self, Self::Error> {
145 let mut errors = Vec::<String>::new();
146 errors.extend(options.plugins.errors.iter().map(Clone::clone));
147 errors.extend(options.presets.errors.iter().map(Clone::clone));
148
149 let typescript = options
150 .presets
151 .typescript
152 .clone()
153 .or_else(|| options.plugins.typescript.clone())
154 .unwrap_or_default();
155
156 let decorator = DecoratorOptions {
157 legacy: options.plugins.legacy_decorator.is_some(),
158 emit_decorator_metadata: options
159 .plugins
160 .legacy_decorator
161 .is_some_and(|o| o.emit_decorator_metadata),
162 };
163
164 let jsx = if let Some(options) = &options.presets.jsx {
165 options.clone()
166 } else {
167 let mut jsx_options = if let Some(options) = &options.plugins.react_jsx_dev {
168 options.clone()
169 } else if let Some(options) = &options.plugins.react_jsx {
170 options.clone()
171 } else {
172 JsxOptions::default()
173 };
174 jsx_options.development = options.plugins.react_jsx_dev.is_some();
175 jsx_options.jsx_plugin = options.plugins.react_jsx.is_some();
176 jsx_options.display_name_plugin = options.plugins.react_display_name;
177 jsx_options.jsx_self_plugin = options.plugins.react_jsx_self;
178 jsx_options.jsx_source_plugin = options.plugins.react_jsx_source;
179 jsx_options
180 };
181
182 let env = options.presets.env.unwrap_or_default();
183
184 let module = Module::try_from(&options.plugins).unwrap_or_else(|_| {
185 options.presets.env.as_ref().map(|env| env.module).unwrap_or_default()
186 });
187
188 let regexp = RegExpOptions {
189 sticky_flag: env.regexp.sticky_flag || options.plugins.sticky_flag,
190 unicode_flag: env.regexp.unicode_flag || options.plugins.unicode_flag,
191 dot_all_flag: env.regexp.dot_all_flag || options.plugins.dot_all_flag,
192 look_behind_assertions: env.regexp.look_behind_assertions
193 || options.plugins.look_behind_assertions,
194 named_capture_groups: env.regexp.named_capture_groups
195 || options.plugins.named_capture_groups,
196 unicode_property_escapes: env.regexp.unicode_property_escapes
197 || options.plugins.unicode_property_escapes,
198 match_indices: env.regexp.match_indices,
199 set_notation: env.regexp.set_notation || options.plugins.set_notation,
200 };
201
202 let es2015 = ES2015Options {
203 arrow_function: options.plugins.arrow_function.or(env.es2015.arrow_function),
204 };
205
206 let es2016 = ES2016Options {
207 exponentiation_operator: options.plugins.exponentiation_operator
208 || env.es2016.exponentiation_operator,
209 };
210
211 let es2017 = ES2017Options {
212 async_to_generator: options.plugins.async_to_generator || env.es2017.async_to_generator,
213 };
214
215 let es2018 = ES2018Options {
216 object_rest_spread: options
217 .plugins
218 .object_rest_spread
219 .or(env.es2018.object_rest_spread),
220 async_generator_functions: options.plugins.async_generator_functions
221 || env.es2018.async_generator_functions,
222 };
223
224 let es2019 = ES2019Options {
225 optional_catch_binding: options.plugins.optional_catch_binding
226 || env.es2019.optional_catch_binding,
227 };
228
229 let es2020 = ES2020Options {
230 export_namespace_from: options.plugins.export_namespace_from
231 || env.es2020.export_namespace_from,
232 optional_chaining: options.plugins.optional_chaining || env.es2020.optional_chaining,
233 nullish_coalescing_operator: options.plugins.nullish_coalescing_operator
234 || env.es2020.nullish_coalescing_operator,
235 big_int: env.es2020.big_int,
236 };
237
238 let es2021 = ES2021Options {
239 logical_assignment_operators: options.plugins.logical_assignment_operators
240 || env.es2021.logical_assignment_operators,
241 };
242
243 let es2022 = ES2022Options {
244 class_static_block: options.plugins.class_static_block || env.es2022.class_static_block,
245 class_properties: options.plugins.class_properties.or(env.es2022.class_properties),
246 };
247
248 if !errors.is_empty() {
249 return Err(errors);
250 }
251
252 let helper_loader = HelperLoaderOptions {
253 mode: if options.external_helpers {
254 HelperLoaderMode::External
255 } else {
256 HelperLoaderMode::default()
257 },
258 ..HelperLoaderOptions::default()
259 };
260
261 let mut plugins = PluginsOptions::default();
262 if let Some(styled_components) = &options.plugins.styled_components {
263 plugins.styled_components = Some(styled_components.clone());
264 }
265
266 Ok(Self {
267 cwd: options.cwd.clone().unwrap_or_default(),
268 assumptions: options.assumptions,
269 typescript,
270 decorator,
271 jsx,
272 env: EnvOptions {
273 module,
274 regexp,
275 es2015,
276 es2016,
277 es2017,
278 es2018,
279 es2019,
280 es2020,
281 es2021,
282 es2022,
283 es2026: ES2026Options {
284 explicit_resource_management: options.plugins.explicit_resource_management,
285 },
286 },
287 proposals: ProposalOptions::default(),
288 helper_loader,
289 plugins,
290 })
291 }
292}