oxc_transformer/options/
mod.rs

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/// <https://babel.dev/docs/options>
35#[derive(Debug, Default, Clone)]
36pub struct TransformOptions {
37    //
38    // Primary Options
39    //
40    /// The working directory that all paths in the programmatic options will be resolved relative to.
41    pub cwd: PathBuf,
42
43    // Core
44    /// Set assumptions in order to produce smaller output.
45    /// For more information, check the [assumptions](https://babel.dev/docs/assumptions) documentation page.
46    pub assumptions: CompilerAssumptions,
47
48    // Plugins
49    /// [preset-typescript](https://babeljs.io/docs/babel-preset-typescript)
50    pub typescript: TypeScriptOptions,
51
52    /// Decorator
53    pub decorator: DecoratorOptions,
54
55    /// Jsx Transform
56    ///
57    /// See [preset-react](https://babeljs.io/docs/babel-preset-react)
58    pub jsx: JsxOptions,
59
60    /// ECMAScript Env Options
61    pub env: EnvOptions,
62
63    /// Proposals
64    pub proposals: ProposalOptions,
65
66    /// Plugins
67    pub plugins: PluginsOptions,
68
69    pub helper_loader: HelperLoaderOptions,
70}
71
72impl TransformOptions {
73    /// Explicitly enable all plugins that are ready, mainly for testing purposes.
74    ///
75    /// NOTE: for internal use only
76    #[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(/* include_unfinished_plugins */ 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    /// Initialize from a comma separated list of `target`s and `environmens`s.
99    ///
100    /// e.g. `es2022,chrome58,edge16`.
101    ///
102    /// # Errors
103    ///
104    /// * Same targets specified multiple times.
105    /// * No matching target.
106    /// * Invalid version.
107    pub fn from_target(s: &str) -> Result<Self, String> {
108        EnvOptions::from_target(s).map(|env| Self { env, ..Self::default() })
109    }
110
111    /// Initialize from a list of `target`s and `environmens`s.
112    ///
113    /// e.g. `["es2020", "chrome58", "edge16", "firefox57", "node12", "safari11"]`.
114    ///
115    /// `target`: `es5`, `es2015` ... `es2024`, `esnext`.
116    /// `environment`: `chrome`, `deno`, `edge`, `firefox`, `hermes`, `ie`, `ios`, `node`, `opera`, `rhino`, `safari`
117    ///
118    /// <https://esbuild.github.io/api/#target>
119    ///
120    /// # Errors
121    ///
122    /// * Same targets specified multiple times.
123    /// * No matching target.
124    /// * Invalid version.
125    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    /// If the `options` contains any unknown fields, they will be returned as a list of errors.
144    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}