Oxc’s transformer provides Babel-compatible code transformations including TypeScript stripping, JSX transformation, and ES2015+ downleveling.
Features
- TypeScript type stripping
- JSX/TSX transformation (React, Preact, automatic runtime)
- ES2015-ES2026 syntax downleveling
- Decorators (Stage 3 proposal)
- Environment-based transforms with browserslist
- React Refresh integration
- Helper function injection
Installation
Add to your Cargo.toml:
[dependencies]
oxc_transformer = "0.116.0"
oxc_parser = "0.116.0"
oxc_semantic = "0.116.0"
oxc_allocator = "0.116.0"
oxc_codegen = "0.116.0"
Basic Usage
use oxc_allocator::Allocator;
use oxc_codegen::Codegen;
use oxc_parser::Parser;
use oxc_semantic::SemanticBuilder;
use oxc_span::SourceType;
use oxc_transformer::{TransformOptions, Transformer};
use std::path::Path;
let allocator = Allocator::default();
let source = "const x: number = 42;";
let source_type = SourceType::ts();
let path = Path::new("test.ts");
// Parse
let parsed = Parser::new(&allocator, source, source_type).parse();
let mut program = parsed.program;
// Build semantic information
let semantic = SemanticBuilder::new()
.with_excess_capacity(2.0) // Reserve space for new bindings
.build(&program);
let scoping = semantic.semantic.into_scoping();
// Transform
let options = TransformOptions::default();
let ret = Transformer::new(&allocator, path, &options)
.build_with_scoping(scoping, &mut program);
// Generate code
let output = Codegen::new().build(&program).code;
println!("{}", output); // const x = 42;
Core Types
Main transformation pipeline.
Constructor:
impl<'a> Transformer<'a> {
pub fn new(
allocator: &'a Allocator,
source_path: &Path,
options: &TransformOptions
) -> Self;
}
Memory arena for allocating transformed AST nodes.
Path to the source file being transformed. Used for source maps and module resolution.
options
&TransformOptions
required
Transformation configuration.
Methods:
impl<'a> Transformer<'a> {
pub fn build_with_scoping(
self,
scoping: Scoping,
program: &mut Program<'a>
) -> TransformerReturn;
}
Configuration for all transformations.
pub struct TransformOptions {
pub typescript: TypeScriptOptions,
pub jsx: JsxOptions,
pub env: EnvOptions,
pub decorator: DecoratorOptions,
pub proposals: ProposalOptions,
pub helper_loader: HelperLoaderOptions,
}
Convenience Methods:
impl TransformOptions {
// Enable all available transforms
pub fn enable_all() -> Self;
// Create from browserslist target
pub fn from_target(target: &str) -> Result<Self>;
// From Babel options
pub fn from(babel_options: &BabelOptions) -> Self;
}
Result of transformation.
pub struct TransformerReturn {
pub errors: Vec<OxcDiagnostic>,
pub scoping: Scoping,
pub helpers_used: FxHashMap<Helper, String>, // Deprecated
}
Diagnostics produced during transformation.
Updated semantic scoping after transformations.
TypeScriptOptions
use oxc_transformer::{TransformOptions, TypeScriptOptions};
let options = TransformOptions {
typescript: TypeScriptOptions {
// Remove all type annotations
..Default::default()
},
..Default::default()
};
Keep class fields with declare keyword.
remove_class_fields_without_initializer
Remove class fields that don’t have an initializer.
Example: Strip TypeScript Types
let allocator = Allocator::default();
let source = r#"
interface User {
name: string;
age: number;
}
const user: User = { name: "Alice", age: 30 };
"#;
let parsed = Parser::new(&allocator, source, SourceType::ts()).parse();
let mut program = parsed.program;
let semantic = SemanticBuilder::new().build(&program).semantic;
let options = TransformOptions::default();
let ret = Transformer::new(&allocator, Path::new("test.ts"), &options)
.build_with_scoping(semantic.into_scoping(), &mut program);
let output = Codegen::new().build(&program).code;
// Output: const user = { name: "Alice", age: 30 };
JsxOptions
use oxc_transformer::{JsxOptions, JsxRuntime};
let jsx_options = JsxOptions {
runtime: JsxRuntime::Automatic, // Use React 17+ automatic runtime
import_source: Some("react".to_string()),
pragma: Some("h".to_string()), // For Preact
pragma_frag: Some("Fragment".to_string()),
..Default::default()
};
let options = TransformOptions {
jsx: jsx_options,
..Default::default()
};
JSX transform mode: Classic (React.createElement) or Automatic (jsx-runtime).
Module to import jsx functions from (default: “react”).
Function to call for JSX elements (classic runtime, e.g., “h” for Preact).
Function to call for fragments (classic runtime).
use oxc_transformer::{JsxOptions, JsxRuntime};
let allocator = Allocator::default();
let source = "const App = () => <div>Hello</div>;";
let parsed = Parser::new(&allocator, source, SourceType::jsx()).parse();
let mut program = parsed.program;
let semantic = SemanticBuilder::new().build(&program).semantic;
let options = TransformOptions {
jsx: JsxOptions {
runtime: JsxRuntime::Classic,
pragma: Some("React.createElement".to_string()),
..Default::default()
},
..Default::default()
};
let ret = Transformer::new(&allocator, Path::new("App.jsx"), &options)
.build_with_scoping(semantic.into_scoping(), &mut program);
let output = Codegen::new().build(&program).code;
// Output: const App = () => React.createElement("div", null, "Hello");
EnvOptions
Transform based on browser/runtime targets:
use oxc_transformer::{EnvOptions, TransformOptions};
// From browserslist query
let env = EnvOptions::from_browserslist_query("last 2 versions")?;
let options = TransformOptions {
env,
..Default::default()
};
Specific browser/runtime versions to target.
Example: Target Specific Browsers
let allocator = Allocator::default();
let source = "const x = a ?? b;";
let parsed = Parser::new(&allocator, source, SourceType::default()).parse();
let mut program = parsed.program;
let semantic = SemanticBuilder::new().build(&program).semantic;
// Transform for older browsers that don't support nullish coalescing
let env = EnvOptions::from_browserslist_query("ie 11")?;
let options = TransformOptions {
env,
..Default::default()
};
let ret = Transformer::new(&allocator, Path::new("test.js"), &options)
.build_with_scoping(semantic.into_scoping(), &mut program);
let output = Codegen::new().build(&program).code;
// Output: const x = a !== null && a !== void 0 ? a : b;
ES2015Options
use oxc_transformer::{ES2015Options, ArrowFunctionsOptions};
let es2015 = ES2015Options {
arrow_functions: Some(ArrowFunctionsOptions {
spec: false, // Don't use .bind() for this binding
}),
};
Supported ES2015+ features:
- Arrow functions
- Classes
- Template literals
- Destructuring
- Spread/rest operators
- Block scoping (let/const)
- And more…
Example: ES2015 Downleveling
let source = "const arrow = (x) => x * 2;";
let parsed = Parser::new(&allocator, source, SourceType::default()).parse();
let mut program = parsed.program;
let semantic = SemanticBuilder::new().build(&program).semantic;
let options = TransformOptions::enable_all();
let ret = Transformer::new(&allocator, Path::new("test.js"), &options)
.build_with_scoping(semantic.into_scoping(), &mut program);
let output = Codegen::new().build(&program).code;
// Output: const arrow = function(x) { return x * 2; };
Decorators
DecoratorOptions
use oxc_transformer::DecoratorOptions;
let options = TransformOptions {
decorator: DecoratorOptions {
version: "2023-11".to_string(),
},
..Default::default()
};
Supports Stage 3 decorators proposal.
React Refresh
ReactRefreshOptions
use oxc_transformer::{JsxOptions, ReactRefreshOptions};
let jsx = JsxOptions {
refresh: Some(ReactRefreshOptions {
refresh_reg: Some("$RefreshReg$".to_string()),
refresh_sig: Some("$RefreshSig$".to_string()),
..Default::default()
}),
..Default::default()
};
Enables React Fast Refresh transformations for development.
Helper Functions
HelperLoaderOptions
use oxc_transformer::{HelperLoaderMode, HelperLoaderOptions};
let helper_loader = HelperLoaderOptions {
mode: HelperLoaderMode::External, // Import from package
// Or: HelperLoaderMode::Inline // Inline helper functions
};
How to inject helper functions: Inline (duplicate in each file) or External (import from package).
Complete Example
use oxc_allocator::Allocator;
use oxc_codegen::Codegen;
use oxc_parser::Parser;
use oxc_semantic::SemanticBuilder;
use oxc_span::SourceType;
use oxc_transformer::{
EnvOptions, JsxOptions, JsxRuntime, TransformOptions, Transformer
};
use std::path::Path;
fn transform_tsx_to_js(source: &str) -> String {
let allocator = Allocator::default();
let path = Path::new("App.tsx");
let source_type = SourceType::tsx();
// Parse
let parsed = Parser::new(&allocator, source, source_type).parse();
let mut program = parsed.program;
// Build semantic info with extra capacity for transforms
let semantic = SemanticBuilder::new()
.with_excess_capacity(2.0)
.build(&program);
// Configure transforms
let env = EnvOptions::from_browserslist_query("last 2 versions").unwrap();
let options = TransformOptions {
typescript: Default::default(),
jsx: JsxOptions {
runtime: JsxRuntime::Automatic,
..Default::default()
},
env,
..Default::default()
};
// Transform
let ret = Transformer::new(&allocator, path, &options)
.build_with_scoping(semantic.semantic.into_scoping(), &mut program);
// Handle errors
for error in ret.errors {
eprintln!("{:?}", error);
}
// Generate output
Codegen::new().build(&program).code
}
let tsx_source = r#"
const Greeting: React.FC<{name: string}> = ({name}) => {
return <div>Hello, {name}!</div>;
};
"#;
let js_output = transform_tsx_to_js(tsx_source);
println!("{}", js_output);
- Always use
SemanticBuilder::with_excess_capacity(2.0) before transforming to avoid reallocations
- Reuse allocators when transforming multiple files by calling
allocator.reset()
- The transformer modifies the AST in-place for efficiency
API Documentation
For complete API documentation, see docs.rs/oxc_transformer.