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 {
91 styled_components: Some(StyledComponentsOptions::default()),
92 tagged_template_transform: true,
93 },
94 helper_loader: HelperLoaderOptions {
95 mode: HelperLoaderMode::Runtime,
96 ..Default::default()
97 },
98 }
99 }
100
101 pub fn from_target(s: &str) -> Result<Self, String> {
111 EnvOptions::from_target(s).map(|env| Self { env, ..Self::default() })
112 }
113
114 pub fn from_target_list<S: AsRef<str>>(list: &[S]) -> Result<Self, String> {
129 EnvOptions::from_target_list(list).map(|env| Self { env, ..Self::default() })
130 }
131}
132
133impl From<ESTarget> for TransformOptions {
134 fn from(target: ESTarget) -> Self {
135 use oxc_compat::ESVersion;
136 let mut engine_targets = EngineTargets::default();
137 engine_targets.insert(Engine::Es, target.version());
138 let env = EnvOptions::from(engine_targets);
139 Self { env, ..Self::default() }
140 }
141}
142
143impl TryFrom<&BabelOptions> for TransformOptions {
144 type Error = Vec<String>;
145
146 fn try_from(options: &BabelOptions) -> Result<Self, Self::Error> {
148 let mut errors = Vec::<String>::new();
149 errors.extend(options.plugins.errors.iter().map(Clone::clone));
150 errors.extend(options.presets.errors.iter().map(Clone::clone));
151
152 let typescript = options
153 .presets
154 .typescript
155 .clone()
156 .or_else(|| options.plugins.typescript.clone())
157 .unwrap_or_default();
158
159 let decorator = DecoratorOptions {
160 legacy: options.plugins.legacy_decorator.is_some(),
161 emit_decorator_metadata: options
162 .plugins
163 .legacy_decorator
164 .is_some_and(|o| o.emit_decorator_metadata),
165 };
166
167 let jsx = if let Some(options) = &options.presets.jsx {
168 options.clone()
169 } else {
170 let mut jsx_options = if let Some(options) = &options.plugins.react_jsx_dev {
171 options.clone()
172 } else if let Some(options) = &options.plugins.react_jsx {
173 options.clone()
174 } else {
175 JsxOptions::default()
176 };
177 jsx_options.development = options.plugins.react_jsx_dev.is_some();
178 jsx_options.jsx_plugin = options.plugins.react_jsx.is_some();
179 jsx_options.display_name_plugin = options.plugins.react_display_name;
180 jsx_options.jsx_self_plugin = options.plugins.react_jsx_self;
181 jsx_options.jsx_source_plugin = options.plugins.react_jsx_source;
182 jsx_options
183 };
184
185 let env = options.presets.env.unwrap_or_default();
186
187 let module = Module::try_from(&options.plugins).unwrap_or_else(|_| {
188 options.presets.env.as_ref().map(|env| env.module).unwrap_or_default()
189 });
190
191 let regexp = RegExpOptions {
192 sticky_flag: env.regexp.sticky_flag || options.plugins.sticky_flag,
193 unicode_flag: env.regexp.unicode_flag || options.plugins.unicode_flag,
194 dot_all_flag: env.regexp.dot_all_flag || options.plugins.dot_all_flag,
195 look_behind_assertions: env.regexp.look_behind_assertions
196 || options.plugins.look_behind_assertions,
197 named_capture_groups: env.regexp.named_capture_groups
198 || options.plugins.named_capture_groups,
199 unicode_property_escapes: env.regexp.unicode_property_escapes
200 || options.plugins.unicode_property_escapes,
201 match_indices: env.regexp.match_indices,
202 set_notation: env.regexp.set_notation || options.plugins.set_notation,
203 };
204
205 let es2015 = ES2015Options {
206 arrow_function: options.plugins.arrow_function.or(env.es2015.arrow_function),
207 };
208
209 let es2016 = ES2016Options {
210 exponentiation_operator: options.plugins.exponentiation_operator
211 || env.es2016.exponentiation_operator,
212 };
213
214 let es2017 = ES2017Options {
215 async_to_generator: options.plugins.async_to_generator || env.es2017.async_to_generator,
216 };
217
218 let es2018 = ES2018Options {
219 object_rest_spread: options
220 .plugins
221 .object_rest_spread
222 .or(env.es2018.object_rest_spread),
223 async_generator_functions: options.plugins.async_generator_functions
224 || env.es2018.async_generator_functions,
225 };
226
227 let es2019 = ES2019Options {
228 optional_catch_binding: options.plugins.optional_catch_binding
229 || env.es2019.optional_catch_binding,
230 };
231
232 let es2020 = ES2020Options {
233 export_namespace_from: options.plugins.export_namespace_from
234 || env.es2020.export_namespace_from,
235 optional_chaining: options.plugins.optional_chaining || env.es2020.optional_chaining,
236 nullish_coalescing_operator: options.plugins.nullish_coalescing_operator
237 || env.es2020.nullish_coalescing_operator,
238 big_int: env.es2020.big_int,
239 arbitrary_module_namespace_names: env.es2020.arbitrary_module_namespace_names,
240 };
241
242 let es2021 = ES2021Options {
243 logical_assignment_operators: options.plugins.logical_assignment_operators
244 || env.es2021.logical_assignment_operators,
245 };
246
247 let es2022 = ES2022Options {
248 class_static_block: options.plugins.class_static_block || env.es2022.class_static_block,
249 class_properties: options.plugins.class_properties.or(env.es2022.class_properties),
250 top_level_await: env.es2022.top_level_await,
251 };
252
253 if !errors.is_empty() {
254 return Err(errors);
255 }
256
257 let helper_loader = HelperLoaderOptions {
258 mode: if options.external_helpers {
259 HelperLoaderMode::External
260 } else {
261 HelperLoaderMode::default()
262 },
263 ..HelperLoaderOptions::default()
264 };
265
266 let mut plugins = PluginsOptions::default();
267 if let Some(styled_components) = &options.plugins.styled_components {
268 plugins.styled_components = Some(styled_components.clone());
269 }
270 plugins.tagged_template_transform = options.plugins.tagged_template_escape;
271
272 Ok(Self {
273 cwd: options.cwd.clone().unwrap_or_default(),
274 assumptions: options.assumptions,
275 typescript,
276 decorator,
277 jsx,
278 env: EnvOptions {
279 module,
280 regexp,
281 es2015,
282 es2016,
283 es2017,
284 es2018,
285 es2019,
286 es2020,
287 es2021,
288 es2022,
289 es2026: ES2026Options {
290 explicit_resource_management: options.plugins.explicit_resource_management,
291 },
292 },
293 proposals: ProposalOptions::default(),
294 helper_loader,
295 plugins,
296 })
297 }
298}