oxc/napi/transform.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
// NOTE: Types must be aligned with [@types/babel__core](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/babel__core/index.d.ts).
#![allow(rustdoc::bare_urls)]
use std::path::PathBuf;
use napi::Either;
use napi_derive::napi;
use rustc_hash::FxHashMap;
use oxc_transformer::{JsxRuntime, RewriteExtensionsMode};
use super::{isolated_declarations::IsolatedDeclarationsOptions, source_map::SourceMap};
#[derive(Default)]
#[napi(object)]
pub struct TransformResult {
/// The transformed code.
///
/// If parsing failed, this will be an empty string.
pub code: String,
/// The source map for the transformed code.
///
/// This will be set if {@link TransformOptions#sourcemap} is `true`.
pub map: Option<SourceMap>,
/// The `.d.ts` declaration file for the transformed code. Declarations are
/// only generated if `declaration` is set to `true` and a TypeScript file
/// is provided.
///
/// If parsing failed and `declaration` is set, this will be an empty string.
///
/// @see {@link TypeScriptOptions#declaration}
/// @see [declaration tsconfig option](https://www.typescriptlang.org/tsconfig/#declaration)
pub declaration: Option<String>,
/// Declaration source map. Only generated if both
/// {@link TypeScriptOptions#declaration declaration} and
/// {@link TransformOptions#sourcemap sourcemap} are set to `true`.
pub declaration_map: Option<SourceMap>,
/// Parse and transformation errors.
///
/// Oxc's parser recovers from common syntax errors, meaning that
/// transformed code may still be available even if there are errors in this
/// list.
pub errors: Vec<String>,
}
/// Options for transforming a JavaScript or TypeScript file.
///
/// @see {@link transform}
#[napi(object)]
#[derive(Default)]
pub struct TransformOptions {
#[napi(ts_type = "'script' | 'module' | 'unambiguous' | undefined")]
pub source_type: Option<String>,
/// Treat the source text as `js`, `jsx`, `ts`, or `tsx`.
#[napi(ts_type = "'js' | 'jsx' | 'ts' | 'tsx'")]
pub lang: Option<String>,
/// The current working directory. Used to resolve relative paths in other
/// options.
pub cwd: Option<String>,
/// Enable source map generation.
///
/// When `true`, the `sourceMap` field of transform result objects will be populated.
///
/// @default false
///
/// @see {@link SourceMap}
pub sourcemap: Option<bool>,
/// Configure how TypeScript is transformed.
pub typescript: Option<TypeScriptOptions>,
/// Configure how TSX and JSX are transformed.
pub jsx: Option<JsxOptions>,
/// Define Plugin
#[napi(ts_type = "Record<string, string>")]
pub define: Option<FxHashMap<String, String>>,
/// Inject Plugin
#[napi(ts_type = "Record<string, string | [string, string]>")]
pub inject: Option<FxHashMap<String, Either<String, Vec<String>>>>,
}
impl From<TransformOptions> for oxc_transformer::TransformOptions {
fn from(options: TransformOptions) -> Self {
Self {
cwd: options.cwd.map(PathBuf::from).unwrap_or_default(),
typescript: options
.typescript
.map(oxc_transformer::TypeScriptOptions::from)
.unwrap_or_default(),
jsx: options.jsx.map(Into::into).unwrap_or_default(),
..Self::default()
}
}
}
#[napi(object)]
#[derive(Default)]
pub struct TypeScriptOptions {
pub jsx_pragma: Option<String>,
pub jsx_pragma_frag: Option<String>,
pub only_remove_type_imports: Option<bool>,
pub allow_namespaces: Option<bool>,
pub allow_declare_fields: Option<bool>,
/// Also generate a `.d.ts` declaration file for TypeScript files.
///
/// The source file must be compliant with all
/// [`isolatedDeclarations`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-5.html#isolated-declarations)
/// requirements.
///
/// @default false
pub declaration: Option<IsolatedDeclarationsOptions>,
/// Rewrite or remove TypeScript import/export declaration extensions.
///
/// - When set to `rewrite`, it will change `.ts`, `.mts`, `.cts` extensions to `.js`, `.mjs`, `.cjs` respectively.
/// - When set to `remove`, it will remove `.ts`/`.mts`/`.cts`/`.tsx` extension entirely.
/// - When set to `true`, it's equivalent to `rewrite`.
/// - When set to `false` or omitted, no changes will be made to the extensions.
///
/// @default false
#[napi(ts_type = "'rewrite' | 'remove' | boolean")]
pub rewrite_import_extensions: Option<Either<bool, String>>,
}
impl From<TypeScriptOptions> for oxc_transformer::TypeScriptOptions {
fn from(options: TypeScriptOptions) -> Self {
let ops = oxc_transformer::TypeScriptOptions::default();
oxc_transformer::TypeScriptOptions {
jsx_pragma: options.jsx_pragma.map(Into::into).unwrap_or(ops.jsx_pragma),
jsx_pragma_frag: options.jsx_pragma_frag.map(Into::into).unwrap_or(ops.jsx_pragma_frag),
only_remove_type_imports: options
.only_remove_type_imports
.unwrap_or(ops.only_remove_type_imports),
allow_namespaces: options.allow_namespaces.unwrap_or(ops.allow_namespaces),
allow_declare_fields: options.allow_declare_fields.unwrap_or(ops.allow_declare_fields),
optimize_const_enums: false,
rewrite_import_extensions: options.rewrite_import_extensions.and_then(|value| {
match value {
Either::A(v) => {
if v {
Some(RewriteExtensionsMode::Rewrite)
} else {
None
}
}
Either::B(v) => match v.as_str() {
"rewrite" => Some(RewriteExtensionsMode::Rewrite),
"remove" => Some(RewriteExtensionsMode::Remove),
_ => None,
},
}
}),
}
}
}
/// Configure how TSX and JSX are transformed.
///
/// @see {@link https://babeljs.io/docs/babel-plugin-transform-react-jsx#options}
#[napi(object)]
pub struct JsxOptions {
/// Decides which runtime to use.
///
/// - 'automatic' - auto-import the correct JSX factories
/// - 'classic' - no auto-import
///
/// @default 'automatic'
#[napi(ts_type = "'classic' | 'automatic'")]
pub runtime: Option<String>,
/// Emit development-specific information, such as `__source` and `__self`.
///
/// @default false
///
/// @see {@link https://babeljs.io/docs/babel-plugin-transform-react-jsx-development}
pub development: Option<bool>,
/// Toggles whether or not to throw an error if an XML namespaced tag name
/// is used.
///
/// Though the JSX spec allows this, it is disabled by default since React's
/// JSX does not currently have support for it.
///
/// @default true
pub throw_if_namespace: Option<bool>,
/// Enables `@babel/plugin-transform-react-pure-annotations`.
///
/// It will mark top-level React method calls as pure for tree shaking.
///
/// @see {@link https://babeljs.io/docs/en/babel-plugin-transform-react-pure-annotations}
///
/// @default true
pub pure: Option<bool>,
/// Replaces the import source when importing functions.
///
/// @default 'react'
pub import_source: Option<String>,
/// Replace the function used when compiling JSX expressions. It should be a
/// qualified name (e.g. `React.createElement`) or an identifier (e.g.
/// `createElement`).
///
/// Only used for `classic` {@link runtime}.
///
/// @default 'React.createElement'
pub pragma: Option<String>,
/// Replace the component used when compiling JSX fragments. It should be a
/// valid JSX tag name.
///
/// Only used for `classic` {@link runtime}.
///
/// @default 'React.Fragment'
pub pragma_frag: Option<String>,
/// When spreading props, use `Object.assign` directly instead of an extend helper.
///
/// Only used for `classic` {@link runtime}.
///
/// @default false
pub use_built_ins: Option<bool>,
/// When spreading props, use inline object with spread elements directly
/// instead of an extend helper or Object.assign.
///
/// Only used for `classic` {@link runtime}.
///
/// @default false
pub use_spread: Option<bool>,
/// Enable React Fast Refresh .
///
/// Conforms to the implementation in {@link https://github.com/facebook/react/tree/main/packages/react-refresh}
///
/// @default false
pub refresh: Option<Either<bool, ReactRefreshOptions>>,
}
impl From<JsxOptions> for oxc_transformer::JsxOptions {
fn from(options: JsxOptions) -> Self {
let ops = oxc_transformer::JsxOptions::default();
oxc_transformer::JsxOptions {
runtime: match options.runtime.as_deref() {
Some("classic") => JsxRuntime::Classic,
/* "automatic" */ _ => JsxRuntime::Automatic,
},
development: options.development.unwrap_or(ops.development),
throw_if_namespace: options.throw_if_namespace.unwrap_or(ops.throw_if_namespace),
pure: options.pure.unwrap_or(ops.pure),
import_source: options.import_source,
pragma: options.pragma,
pragma_frag: options.pragma_frag,
use_built_ins: options.use_built_ins,
use_spread: options.use_spread,
refresh: options.refresh.and_then(|value| match value {
Either::A(b) => b.then(oxc_transformer::ReactRefreshOptions::default),
Either::B(options) => Some(oxc_transformer::ReactRefreshOptions::from(options)),
}),
..Default::default()
}
}
}
#[napi(object)]
pub struct ReactRefreshOptions {
/// Specify the identifier of the refresh registration variable.
///
/// @default `$RefreshReg$`.
pub refresh_reg: Option<String>,
/// Specify the identifier of the refresh signature variable.
///
/// @default `$RefreshSig$`.
pub refresh_sig: Option<String>,
pub emit_full_signatures: Option<bool>,
}
impl From<ReactRefreshOptions> for oxc_transformer::ReactRefreshOptions {
fn from(options: ReactRefreshOptions) -> Self {
let ops = oxc_transformer::ReactRefreshOptions::default();
oxc_transformer::ReactRefreshOptions {
refresh_reg: options.refresh_reg.unwrap_or(ops.refresh_reg),
refresh_sig: options.refresh_sig.unwrap_or(ops.refresh_sig),
emit_full_signatures: options.emit_full_signatures.unwrap_or(ops.emit_full_signatures),
}
}
}
#[napi(object)]
pub struct ArrowFunctionsOptions {
/// This option enables the following:
/// * Wrap the generated function in .bind(this) and keeps uses of this inside the function as-is, instead of using a renamed this.
/// * Add a runtime check to ensure the functions are not instantiated.
/// * Add names to arrow functions.
///
/// @default false
pub spec: Option<bool>,
}
impl From<ArrowFunctionsOptions> for oxc_transformer::ArrowFunctionsOptions {
fn from(options: ArrowFunctionsOptions) -> Self {
oxc_transformer::ArrowFunctionsOptions { spec: options.spec.unwrap_or_default() }
}
}
#[napi(object)]
pub struct Es2015Options {
/// Transform arrow functions into function expressions.
pub arrow_function: Option<ArrowFunctionsOptions>,
}
impl From<Es2015Options> for oxc_transformer::ES2015Options {
fn from(options: Es2015Options) -> Self {
oxc_transformer::ES2015Options { arrow_function: options.arrow_function.map(Into::into) }
}
}