feat(transform-react): expose TypeScript transform options

Original PR: #26899

Closes #26864
This commit is contained in:
Tomas Särnhammar authored and Boshen committed 2026-10-29 15:21:32 +08:00
1 parent 224f4ddd08
commit 8dd67a61c0
17 files changed
+342

No files matched your search

+29
View File
@@ -360,6 +360,12 @@ export interface TransformOptions {
* @see <https://oxc.rs/docs/guide/usage/transformer/jsx>
*/
jsx?: 'preserve' | JsxOptions
/**
* Configure how Typescript is transformed.
*
* @see <https://oxc.rs/docs/guide/usage/transformer/typescript>
*/
typescript?: TypeScriptOptions
/**
* Configure React Compiler, or disable it with `false`.
*
@@ -397,3 +403,26 @@ export interface TransformResult {
* removed and configured JSX transforms run afterwards.
*/
export declare function transformSync(filename: string, sourceText: string, options?: TransformOptions | undefined | null): TransformResult
/**
* Configure how Typescript is transformed.
*
* @see <https://oxc.rs/docs/guide/usage/transformer/typescript>
*/
export interface TypeScriptOptions {
/**
* Inlines const enum values and removes the declaration.
*
* @default false
*/
optimizeConstEnums?: boolean
/**
* Inlines regular (non-const) enum member accesses when all members
* satisfy const enum constraints (i.e., their values are statically evaluable).
* Non-exported enum declarations are also removed when all members are
* evaluable and no references to the enum as a runtime value exist.
*
* @default false
*/
optimizeEnums?: boolean
}
+45
View File
@@ -36,6 +36,12 @@ pub struct TransformOptions {
#[napi(ts_type = "'preserve' | JsxOptions")]
pub jsx: Option<Either<String, JsxOptions>>,
/// Configure how Typescript is transformed.
///
/// @see <https://oxc.rs/docs/guide/usage/transformer/typescript>
#[napi(ts_type = "TypeScriptOptions")]
pub typescript: Option<TypeScriptOptions>,
/// Configure React Compiler, or disable it with `false`.
///
/// @default true
@@ -188,6 +194,26 @@ pub struct JsxOptions {
pub refresh: Option<Either<bool, ReactRefreshOptions>>,
}
/// Configure how Typescript is transformed.
///
/// @see <https://oxc.rs/docs/guide/usage/transformer/typescript>
#[napi(object)]
#[derive(Debug)]
pub struct TypeScriptOptions {
/// Inlines const enum values and removes the declaration.
///
/// @default false
pub optimize_const_enums: Option<bool>,
/// Inlines regular (non-const) enum member accesses when all members
/// satisfy const enum constraints (i.e., their values are statically evaluable).
/// Non-exported enum declarations are also removed when all members are
/// evaluable and no references to the enum as a runtime value exist.
///
/// @default false
pub optimize_enums: Option<bool>,
}
/// React Fast Refresh options.
#[napi(object)]
#[derive(Debug)]
@@ -313,10 +339,16 @@ impl TransformOptions {
Some(Either::B(options)) => oxc::transformer::JsxOptions::from(options),
};
let typescript: oxc::transformer::TypeScriptOptions = match self.typescript {
None => oxc::transformer::TypeScriptOptions::default(),
Some(options) => oxc::transformer::TypeScriptOptions::from(options),
};
Ok((
react_compiler,
oxc::transformer::TransformOptions {
jsx,
typescript,
..oxc::transformer::TransformOptions::default()
},
))
@@ -426,6 +458,19 @@ impl From<JsxOptions> for oxc::transformer::JsxOptions {
}
}
impl From<TypeScriptOptions> for oxc::transformer::TypeScriptOptions {
fn from(options: TypeScriptOptions) -> Self {
let defaults = Self::default();
Self {
optimize_const_enums: options
.optimize_const_enums
.unwrap_or(defaults.optimize_const_enums),
optimize_enums: options.optimize_enums.unwrap_or(defaults.optimize_enums),
..defaults
}
}
}
impl From<ReactRefreshOptions> for oxc::transformer::ReactRefreshOptions {
fn from(options: ReactRefreshOptions) -> Self {
let defaults = Self::default();
@@ -0,0 +1,28 @@
import { existsSync, readdirSync, readFileSync } from "node:fs";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
import { transformSync, type TransformOptions, type TransformResult } from "../index";
const fixturesDir = join(import.meta.dirname, "fixtures");
const fixtures = readdirSync(fixturesDir).sort();
function render(result: TransformResult): string {
const errors = result.errors.map((error) => `${error.severity}: ${error.message}`).join("\n");
return `## Code\n\n${result.code}\n## Errors\n\n${errors || "(none)"}\n\n## Fatal\n\n${result.fatal}\n`;
}
describe("fixtures", () => {
it.each(fixtures)("%s", async (name) => {
const dir = join(fixturesDir, name);
const input = readdirSync(dir).find((file) => file.startsWith("input."));
if (input === undefined) throw new Error(`${name} has no input file`);
const optionsPath = join(dir, "options.json");
const options: TransformOptions = existsSync(optionsPath)
? JSON.parse(readFileSync(optionsPath, "utf8"))
: {};
const result = transformSync(input, readFileSync(join(dir, input), "utf8"), options);
await expect(render(result)).toMatchFileSnapshot(join(dir, "output.snap"));
});
});
@@ -0,0 +1,16 @@
enum Direction {
Up = 1,
Down = 2,
}
const enum Status {
Active = "active",
}
export function App() {
return (
<div>
{Direction.Up}:{Status.Active}
</div>
);
}
@@ -0,0 +1,3 @@
{
"reactCompiler": false
}
@@ -0,0 +1,27 @@
## Code
import { jsxs as _jsxs } from "react/jsx-runtime";
var Direction = /* @__PURE__ */ function(Direction) {
Direction[Direction["Up"] = 1] = "Up";
Direction[Direction["Down"] = 2] = "Down";
return Direction;
}(Direction || {});
var Status = /* @__PURE__ */ function(Status) {
Status["Active"] = "active";
return Status;
}(Status || {});
export function App() {
return /* @__PURE__ */ _jsxs("div", { children: [
Direction.Up,
":",
Status.Active
] });
}
## Errors
(none)
## Fatal
false
@@ -0,0 +1,16 @@
enum Direction {
Up = 1,
Down = 2,
}
const enum Status {
Active = "active",
}
export function App() {
return (
<div>
{Direction.Up}:{Status.Active}
</div>
);
}
@@ -0,0 +1,7 @@
{
"reactCompiler": false,
"typescript": {
"optimizeConstEnums": true,
"optimizeEnums": false
}
}
@@ -0,0 +1,23 @@
## Code
import { jsxs as _jsxs } from "react/jsx-runtime";
var Direction = /* @__PURE__ */ function(Direction) {
Direction[Direction["Up"] = 1] = "Up";
Direction[Direction["Down"] = 2] = "Down";
return Direction;
}(Direction || {});
export function App() {
return /* @__PURE__ */ _jsxs("div", { children: [
Direction.Up,
":",
"active"
] });
}
## Errors
(none)
## Fatal
false
@@ -0,0 +1,16 @@
enum Direction {
Up = 1,
Down = 2,
}
const enum Status {
Active = "active",
}
export function App() {
return (
<div>
{Direction.Up}:{Status.Active}
</div>
);
}
@@ -0,0 +1,6 @@
{
"typescript": {
"optimizeConstEnums": true,
"optimizeEnums": true
}
}
@@ -0,0 +1,27 @@
## Code
import { c as _c } from "react/compiler-runtime";
import { jsxs as _jsxs } from "react/jsx-runtime";
export function App() {
const $ = _c(1);
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = /* @__PURE__ */ _jsxs("div", { children: [
1,
":",
"active"
] });
$[0] = t0;
} else {
t0 = $[0];
}
return t0;
}
## Errors
(none)
## Fatal
false
@@ -0,0 +1,16 @@
enum Direction {
Up = 1,
Down = 2,
}
const enum Status {
Active = "active",
}
export function App() {
return (
<div>
{Direction.Up}:{Status.Active}
</div>
);
}
@@ -0,0 +1,7 @@
{
"reactCompiler": false,
"typescript": {
"optimizeConstEnums": true,
"optimizeEnums": true
}
}
@@ -0,0 +1,18 @@
## Code
import { jsxs as _jsxs } from "react/jsx-runtime";
export function App() {
return /* @__PURE__ */ _jsxs("div", { children: [
1,
":",
"active"
] });
}
## Errors
(none)
## Fatal
false
@@ -355,6 +355,12 @@ export interface TransformOptions {
* @see <https://oxc.rs/docs/guide/usage/transformer/jsx>
*/
jsx?: 'preserve' | JsxOptions
/**
* Configure how Typescript is transformed.
*
* @see <https://oxc.rs/docs/guide/usage/transformer/typescript>
*/
typescript?: TypeScriptOptions
/**
* Configure React Compiler, or disable it with `false`.
*
@@ -392,3 +398,26 @@ export interface TransformResult {
* removed and configured JSX transforms run afterwards.
*/
export declare function transformSync(filename: string, sourceText: string, options?: TransformOptions | undefined | null): TransformResult
/**
* Configure how Typescript is transformed.
*
* @see <https://oxc.rs/docs/guide/usage/transformer/typescript>
*/
export interface TypeScriptOptions {
/**
* Inlines const enum values and removes the declaration.
*
* @default false
*/
optimizeConstEnums?: boolean
/**
* Inlines regular (non-const) enum member accesses when all members
* satisfy const enum constraints (i.e., their values are statically evaluable).
* Non-exported enum declarations are also removed when all members are
* evaluable and no references to the enum as a runtime value exist.
*
* @default false
*/
optimizeEnums?: boolean
}
@@ -355,6 +355,12 @@ export interface TransformOptions {
* @see <https://oxc.rs/docs/guide/usage/transformer/jsx>
*/
jsx?: 'preserve' | JsxOptions
/**
* Configure how Typescript is transformed.
*
* @see <https://oxc.rs/docs/guide/usage/transformer/typescript>
*/
typescript?: TypeScriptOptions
/**
* Configure React Compiler, or disable it with `false`.
*
@@ -392,3 +398,26 @@ export interface TransformResult {
* removed and configured JSX transforms run afterwards.
*/
export declare function transformSync(filename: string, sourceText: string, options?: TransformOptions | undefined | null): TransformResult
/**
* Configure how Typescript is transformed.
*
* @see <https://oxc.rs/docs/guide/usage/transformer/typescript>
*/
export interface TypeScriptOptions {
/**
* Inlines const enum values and removes the declaration.
*
* @default false
*/
optimizeConstEnums?: boolean
/**
* Inlines regular (non-const) enum member accesses when all members
* satisfy const enum constraints (i.e., their values are statically evaluable).
* Non-exported enum declarations are also removed when all members are
* evaluable and no references to the enum as a runtime value exist.
*
* @default false
*/
optimizeEnums?: boolean
}