mirror of
https://github.com/aphrody-labs/oxc.git
synced 2026-10-15 06:39:17 +00:00
feat(napi): restructure React transform options (#25326)
Restructure `oxc-transform-react` options around `reactCompiler` and the shared JSX/Fast Refresh configuration. Generated with AI assistance; reviewed and validated by the contributor.
This commit is contained in:
6 files changed
+550
-83
No files matched your search
@@ -4,7 +4,7 @@ Native Node.js bindings for Oxc's experimental Rust port of React Compiler.
|
||||
|
||||
The API follows `oxc-transform`: pass a filename, source text, and optional
|
||||
options to either `transformSync` or `transform`. React Compiler runs first,
|
||||
then Oxc removes TypeScript syntax and lowers JSX.
|
||||
then Oxc removes TypeScript syntax and applies the configured JSX transforms.
|
||||
|
||||
```javascript
|
||||
import { transformSync } from "oxc-transform-react";
|
||||
@@ -17,7 +17,12 @@ const result = transformSync(
|
||||
}
|
||||
`,
|
||||
{
|
||||
target: "19",
|
||||
reactCompiler: {
|
||||
target: "19",
|
||||
},
|
||||
jsx: {
|
||||
runtime: "automatic",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
@@ -33,11 +38,16 @@ the downstream transform. Some React Compiler bail-outs have error severity but
|
||||
are nonfatal under the default `panicThreshold`; check `fatal` to decide whether
|
||||
the transform emitted usable code.
|
||||
|
||||
The React Compiler options use the same names as
|
||||
`reactCompiler` defaults to `true`. Set it to `false` to skip React Compiler,
|
||||
or pass an options object using the same names as
|
||||
`babel-plugin-react-compiler`/`react-compiler-napi`, including
|
||||
`compilationMode`, `panicThreshold`, `target`, `gating`, `outputMode`,
|
||||
suppression controls, and the supported `environment` flags.
|
||||
|
||||
`jsx` accepts the same options as `oxc-transform`, including automatic or
|
||||
classic runtime configuration and React Fast Refresh. Set it to `"preserve"`
|
||||
to leave JSX syntax in the output.
|
||||
|
||||
Callback-valued options such as `logger`, function-valued `sources`, and type
|
||||
provider callbacks are not accepted by the native binding. `sources` accepts
|
||||
an array of filename substrings instead.
|
||||
Vendored
+137
-25
@@ -36,6 +36,76 @@ export interface SourceMap {
|
||||
version: number
|
||||
x_google_ignoreList?: Array<number>
|
||||
}
|
||||
/**
|
||||
* Configure how TSX and JSX are transformed.
|
||||
*
|
||||
* @see <https://oxc.rs/docs/guide/usage/transformer/jsx>
|
||||
*/
|
||||
export interface JsxOptions {
|
||||
/**
|
||||
* Decides which runtime to use.
|
||||
*
|
||||
* - 'automatic' - auto-import the correct JSX factories
|
||||
* - 'classic' - no auto-import
|
||||
*
|
||||
* @default 'automatic'
|
||||
*/
|
||||
runtime?: 'classic' | 'automatic'
|
||||
/**
|
||||
* Emit development-specific information, such as `__source` and `__self`.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
development?: boolean
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
throwIfNamespace?: boolean
|
||||
/**
|
||||
* Mark JSX elements and top-level React method calls as pure for tree shaking.
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
pure?: boolean
|
||||
/**
|
||||
* Replaces the import source when importing functions.
|
||||
*
|
||||
* @default 'react'
|
||||
*/
|
||||
importSource?: 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'
|
||||
*/
|
||||
pragma?: 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'
|
||||
*/
|
||||
pragmaFrag?: string
|
||||
/**
|
||||
* Enable React Fast Refresh.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
refresh?: boolean | ReactRefreshOptions
|
||||
}
|
||||
|
||||
/** Dynamic feature-gating import. */
|
||||
export interface ReactCompilerDynamicGating {
|
||||
source: string
|
||||
@@ -97,31 +167,11 @@ export interface ReactCompilerMetaTarget {
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a JavaScript or TypeScript React module asynchronously.
|
||||
* React Compiler options.
|
||||
*
|
||||
* This uses a worker-pool thread and can be slower than `transformSync` for a
|
||||
* single small module.
|
||||
* Fields mirror `babel-plugin-react-compiler` and `react-compiler-napi`.
|
||||
*/
|
||||
export declare function transform(filename: string, sourceText: string, options?: TransformOptions | undefined | null): Promise<TransformResult>
|
||||
|
||||
/**
|
||||
* Options for compiling a JavaScript or TypeScript React module.
|
||||
*
|
||||
* React Compiler fields mirror `babel-plugin-react-compiler` and
|
||||
* `react-compiler-napi`. `lang`, `sourceType`, and `sourcemap` configure the
|
||||
* surrounding Oxc parse/codegen pipeline.
|
||||
*/
|
||||
export interface TransformOptions {
|
||||
/** Treat the source as `js`, `jsx`, `ts`, `tsx`, or `dts`. */
|
||||
lang?: 'js' | 'jsx' | 'ts' | 'tsx' | 'dts'
|
||||
/** Treat the source as script, module, CommonJS, or infer it from syntax. */
|
||||
sourceType?: 'script' | 'module' | 'commonjs' | 'unambiguous'
|
||||
/**
|
||||
* Generate a source map.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
sourcemap?: boolean
|
||||
export interface ReactCompilerOptions {
|
||||
/**
|
||||
* Which functions the compiler attempts to compile.
|
||||
*
|
||||
@@ -184,6 +234,68 @@ export interface TransformOptions {
|
||||
environment?: ReactCompilerEnvironmentOptions
|
||||
}
|
||||
|
||||
/** React Fast Refresh options. */
|
||||
export interface ReactRefreshOptions {
|
||||
/**
|
||||
* Specify the identifier of the refresh registration variable.
|
||||
*
|
||||
* @default `$RefreshReg$`
|
||||
*/
|
||||
refreshReg?: string
|
||||
/**
|
||||
* Specify the identifier of the refresh signature variable.
|
||||
*
|
||||
* @default `$RefreshSig$`
|
||||
*/
|
||||
refreshSig?: string
|
||||
/**
|
||||
* Emit full hook signatures instead of compact hashes.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
emitFullSignatures?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a JavaScript or TypeScript React module asynchronously.
|
||||
*
|
||||
* This uses a worker-pool thread and can be slower than `transformSync` for a
|
||||
* single small module.
|
||||
*/
|
||||
export declare function transform(filename: string, sourceText: string, options?: TransformOptions | undefined | null): Promise<TransformResult>
|
||||
|
||||
/**
|
||||
* Options for compiling a JavaScript or TypeScript React module.
|
||||
*
|
||||
* `lang`, `sourceType`, and `sourcemap` configure the surrounding Oxc
|
||||
* parse/codegen pipeline. React Compiler and JSX transforms are configured
|
||||
* independently.
|
||||
*/
|
||||
export interface TransformOptions {
|
||||
/** Treat the source as `js`, `jsx`, `ts`, `tsx`, or `dts`. */
|
||||
lang?: 'js' | 'jsx' | 'ts' | 'tsx' | 'dts'
|
||||
/** Treat the source as script, module, CommonJS, or infer it from syntax. */
|
||||
sourceType?: 'script' | 'module' | 'commonjs' | 'unambiguous'
|
||||
/**
|
||||
* Generate a source map.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
sourcemap?: boolean
|
||||
/**
|
||||
* Configure how TSX and JSX are transformed, or preserve JSX syntax.
|
||||
*
|
||||
* @see <https://oxc.rs/docs/guide/usage/transformer/jsx>
|
||||
*/
|
||||
jsx?: 'preserve' | JsxOptions
|
||||
/**
|
||||
* Configure React Compiler, or disable it with `false`.
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
reactCompiler?: boolean | ReactCompilerOptions
|
||||
}
|
||||
|
||||
/** Result returned by the React Compiler transform. */
|
||||
export interface TransformResult {
|
||||
/** Whether the transform was aborted without emitting code. */
|
||||
@@ -204,7 +316,7 @@ export interface TransformResult {
|
||||
/**
|
||||
* Compile a JavaScript or TypeScript React module synchronously.
|
||||
*
|
||||
* The React Compiler runs first on the pristine AST. TypeScript and JSX are
|
||||
* lowered afterwards, matching the transform pipeline used by `oxc-transform`.
|
||||
* The React Compiler runs first on the pristine AST. TypeScript syntax is
|
||||
* removed and configured JSX transforms run afterwards.
|
||||
*/
|
||||
export declare function transformSync(filename: string, sourceText: string, options?: TransformOptions | undefined | null): TransformResult
|
||||
@@ -149,8 +149,8 @@ fn error_result(filename: &str, source_text: &str, diagnostics: Diagnostics) ->
|
||||
|
||||
/// Compile a JavaScript or TypeScript React module synchronously.
|
||||
///
|
||||
/// The React Compiler runs first on the pristine AST. TypeScript and JSX are
|
||||
/// lowered afterwards, matching the transform pipeline used by `oxc-transform`.
|
||||
/// The React Compiler runs first on the pristine AST. TypeScript syntax is
|
||||
/// removed and configured JSX transforms run afterwards.
|
||||
#[napi]
|
||||
pub fn transform_sync(
|
||||
filename: String,
|
||||
|
||||
@@ -11,9 +11,9 @@ use oxc_react_compiler::{
|
||||
|
||||
/// Options for compiling a JavaScript or TypeScript React module.
|
||||
///
|
||||
/// React Compiler fields mirror `babel-plugin-react-compiler` and
|
||||
/// `react-compiler-napi`. `lang`, `sourceType`, and `sourcemap` configure the
|
||||
/// surrounding Oxc parse/codegen pipeline.
|
||||
/// `lang`, `sourceType`, and `sourcemap` configure the surrounding Oxc
|
||||
/// parse/codegen pipeline. React Compiler and JSX transforms are configured
|
||||
/// independently.
|
||||
#[napi(object)]
|
||||
#[derive(Default, Debug)]
|
||||
pub struct TransformOptions {
|
||||
@@ -30,6 +30,25 @@ pub struct TransformOptions {
|
||||
/// @default false
|
||||
pub sourcemap: Option<bool>,
|
||||
|
||||
/// Configure how TSX and JSX are transformed, or preserve JSX syntax.
|
||||
///
|
||||
/// @see <https://oxc.rs/docs/guide/usage/transformer/jsx>
|
||||
#[napi(ts_type = "'preserve' | JsxOptions")]
|
||||
pub jsx: Option<Either<String, JsxOptions>>,
|
||||
|
||||
/// Configure React Compiler, or disable it with `false`.
|
||||
///
|
||||
/// @default true
|
||||
#[napi(ts_type = "boolean | ReactCompilerOptions")]
|
||||
pub react_compiler: Option<Either<bool, ReactCompilerOptions>>,
|
||||
}
|
||||
|
||||
/// React Compiler options.
|
||||
///
|
||||
/// Fields mirror `babel-plugin-react-compiler` and `react-compiler-napi`.
|
||||
#[napi(object)]
|
||||
#[derive(Default, Debug)]
|
||||
pub struct ReactCompilerOptions {
|
||||
/// Which functions the compiler attempts to compile.
|
||||
///
|
||||
/// @default 'infer'
|
||||
@@ -92,6 +111,88 @@ pub struct TransformOptions {
|
||||
pub environment: Option<ReactCompilerEnvironmentOptions>,
|
||||
}
|
||||
|
||||
/// Configure how TSX and JSX are transformed.
|
||||
///
|
||||
/// @see <https://oxc.rs/docs/guide/usage/transformer/jsx>
|
||||
#[napi(object)]
|
||||
#[derive(Debug)]
|
||||
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
|
||||
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>,
|
||||
|
||||
/// Mark JSX elements and top-level React method calls as pure for tree shaking.
|
||||
///
|
||||
/// @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>,
|
||||
|
||||
/// Enable React Fast Refresh.
|
||||
///
|
||||
/// @default false
|
||||
pub refresh: Option<Either<bool, ReactRefreshOptions>>,
|
||||
}
|
||||
|
||||
/// React Fast Refresh options.
|
||||
#[napi(object)]
|
||||
#[derive(Debug)]
|
||||
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>,
|
||||
|
||||
/// Emit full hook signatures instead of compact hashes.
|
||||
///
|
||||
/// @default false
|
||||
pub emit_full_signatures: Option<bool>,
|
||||
}
|
||||
|
||||
/// Meta-internal React runtime target.
|
||||
#[napi(object)]
|
||||
#[derive(Debug)]
|
||||
@@ -165,20 +266,39 @@ impl TransformOptions {
|
||||
self,
|
||||
filename: &str,
|
||||
) -> Result<(Option<PluginOptions>, oxc::transformer::TransformOptions), OxcDiagnostic> {
|
||||
let enabled = self
|
||||
.sources
|
||||
.as_ref()
|
||||
.is_none_or(|sources| sources.iter().any(|source| filename.contains(source.as_str())));
|
||||
let react_compiler = enabled.then(|| self.into_plugin_options()).transpose()?;
|
||||
let react_compiler = match self.react_compiler {
|
||||
None | Some(Either::A(true)) => Some(PluginOptions::default()),
|
||||
Some(Either::A(false)) => None,
|
||||
Some(Either::B(options)) => options.resolve(filename)?,
|
||||
};
|
||||
|
||||
let jsx = match self.jsx {
|
||||
None => oxc::transformer::JsxOptions::enable(),
|
||||
Some(Either::A(value)) if value == "preserve" => {
|
||||
oxc::transformer::JsxOptions::disable()
|
||||
}
|
||||
Some(Either::A(value)) => return Err(invalid_jsx_option(&value)),
|
||||
Some(Either::B(options)) => oxc::transformer::JsxOptions::from(options),
|
||||
};
|
||||
|
||||
Ok((
|
||||
react_compiler,
|
||||
oxc::transformer::TransformOptions {
|
||||
jsx: oxc::transformer::JsxOptions::enable(),
|
||||
jsx,
|
||||
..oxc::transformer::TransformOptions::default()
|
||||
},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl ReactCompilerOptions {
|
||||
fn resolve(self, filename: &str) -> Result<Option<PluginOptions>, OxcDiagnostic> {
|
||||
let enabled = self
|
||||
.sources
|
||||
.as_ref()
|
||||
.is_none_or(|sources| sources.iter().any(|source| filename.contains(source.as_str())));
|
||||
enabled.then(|| self.into_plugin_options()).transpose()
|
||||
}
|
||||
|
||||
fn into_plugin_options(self) -> Result<PluginOptions, OxcDiagnostic> {
|
||||
let mut options = PluginOptions::default();
|
||||
@@ -245,6 +365,44 @@ impl TransformOptions {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<JsxOptions> for oxc::transformer::JsxOptions {
|
||||
fn from(options: JsxOptions) -> Self {
|
||||
let defaults = Self::default();
|
||||
Self {
|
||||
runtime: match options.runtime.as_deref() {
|
||||
Some("classic") => oxc::transformer::JsxRuntime::Classic,
|
||||
/* "automatic" */ _ => oxc::transformer::JsxRuntime::Automatic,
|
||||
},
|
||||
development: options.development.unwrap_or(defaults.development),
|
||||
throw_if_namespace: options.throw_if_namespace.unwrap_or(defaults.throw_if_namespace),
|
||||
pure: options.pure.unwrap_or(defaults.pure),
|
||||
import_source: options.import_source,
|
||||
pragma: options.pragma,
|
||||
pragma_frag: options.pragma_frag,
|
||||
use_built_ins: None,
|
||||
use_spread: None,
|
||||
refresh: options.refresh.and_then(|value| match value {
|
||||
Either::A(enabled) => enabled.then(oxc::transformer::ReactRefreshOptions::default),
|
||||
Either::B(options) => Some(oxc::transformer::ReactRefreshOptions::from(options)),
|
||||
}),
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ReactRefreshOptions> for oxc::transformer::ReactRefreshOptions {
|
||||
fn from(options: ReactRefreshOptions) -> Self {
|
||||
let defaults = Self::default();
|
||||
Self {
|
||||
refresh_reg: options.refresh_reg.unwrap_or(defaults.refresh_reg),
|
||||
refresh_sig: options.refresh_sig.unwrap_or(defaults.refresh_sig),
|
||||
emit_full_signatures: options
|
||||
.emit_full_signatures
|
||||
.unwrap_or(defaults.emit_full_signatures),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ReactCompilerEnvironmentOptions {
|
||||
fn apply_to(self, environment: &mut EnvironmentConfig) -> Result<(), OxcDiagnostic> {
|
||||
macro_rules! apply_bool_options {
|
||||
@@ -328,3 +486,7 @@ fn parse<T: FromStr>(value: &str, option: &str) -> Result<T, OxcDiagnostic> {
|
||||
fn invalid_option(option: &str, value: &str) -> OxcDiagnostic {
|
||||
OxcDiagnostic::error(format!("Invalid React Compiler `{option}` option: `{value}`."))
|
||||
}
|
||||
|
||||
fn invalid_jsx_option(value: &str) -> OxcDiagnostic {
|
||||
OxcDiagnostic::error(format!("Invalid `jsx` option: `{value}`."))
|
||||
}
|
||||
@@ -29,14 +29,18 @@ describe("transformSync", () => {
|
||||
});
|
||||
|
||||
it("forwards React Compiler options", () => {
|
||||
const target = transformSync("Component.tsx", fixture, { target: "18" });
|
||||
const target = transformSync("Component.tsx", fixture, {
|
||||
reactCompiler: { target: "18" },
|
||||
});
|
||||
expect(target.errors).toEqual([]);
|
||||
expect(target.code).toContain("react-compiler-runtime");
|
||||
|
||||
const gated = transformSync("Component.tsx", fixture, {
|
||||
gating: {
|
||||
source: "feature-flags",
|
||||
importSpecifierName: "isCompilerEnabled",
|
||||
reactCompiler: {
|
||||
gating: {
|
||||
source: "feature-flags",
|
||||
importSpecifierName: "isCompilerEnabled",
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(gated.errors).toEqual([]);
|
||||
@@ -52,7 +56,9 @@ describe("transformSync", () => {
|
||||
return <div>{props.text}</div>;
|
||||
}`,
|
||||
{
|
||||
dynamicGating: { source: "dynamic-feature-flags" },
|
||||
reactCompiler: {
|
||||
dynamicGating: { source: "dynamic-feature-flags" },
|
||||
},
|
||||
},
|
||||
);
|
||||
expect(dynamic.errors).toEqual([]);
|
||||
@@ -60,9 +66,11 @@ describe("transformSync", () => {
|
||||
expect(dynamic.code).toContain("isCompilerEnabled");
|
||||
|
||||
const meta = transformSync("Component.tsx", fixture, {
|
||||
target: {
|
||||
kind: "donotuse_meta_internal",
|
||||
runtimeModule: "custom-react-runtime",
|
||||
reactCompiler: {
|
||||
target: {
|
||||
kind: "donotuse_meta_internal",
|
||||
runtimeModule: "custom-react-runtime",
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(meta.errors).toEqual([]);
|
||||
@@ -80,7 +88,7 @@ describe("transformSync", () => {
|
||||
expect(optedOut.code).not.toContain("_c(");
|
||||
|
||||
const compiled = transformSync("Component.jsx", source, {
|
||||
ignoreUseNoForget: true,
|
||||
reactCompiler: { ignoreUseNoForget: true },
|
||||
});
|
||||
expect(compiled.errors).toEqual([]);
|
||||
expect(compiled.code).toContain("_c(");
|
||||
@@ -100,7 +108,9 @@ describe("transformSync", () => {
|
||||
},
|
||||
],
|
||||
])("reports an invalid %s option without emitting code", (option, options) => {
|
||||
const result = transformSync("Component.tsx", fixture, options as never);
|
||||
const result = transformSync("Component.tsx", fixture, {
|
||||
reactCompiler: options,
|
||||
} as never);
|
||||
expect(result.fatal).toBe(true);
|
||||
expect(result.code).toBe("");
|
||||
expect(result.errors).toHaveLength(1);
|
||||
@@ -131,7 +141,7 @@ describe("transformSync", () => {
|
||||
|
||||
it("can filter files with sources", () => {
|
||||
const result = transformSync("vendor/Component.tsx", fixture, {
|
||||
sources: ["src/"],
|
||||
reactCompiler: { sources: ["src/"] },
|
||||
});
|
||||
|
||||
expect(result.errors).toEqual([]);
|
||||
@@ -180,8 +190,10 @@ describe("transformSync", () => {
|
||||
return <div>{doubled}</div>;
|
||||
}`,
|
||||
{
|
||||
environment: {
|
||||
validateExhaustiveMemoizationDependencies: false,
|
||||
reactCompiler: {
|
||||
environment: {
|
||||
validateExhaustiveMemoizationDependencies: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
@@ -235,8 +247,10 @@ describe("transformSync", () => {
|
||||
return <span>{props.text}</span>;
|
||||
}`,
|
||||
{
|
||||
environment: {
|
||||
validateExhaustiveMemoizationDependencies: false,
|
||||
reactCompiler: {
|
||||
environment: {
|
||||
validateExhaustiveMemoizationDependencies: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
@@ -260,9 +274,11 @@ describe("transformSync", () => {
|
||||
return <div>{doubled}</div>;
|
||||
}`,
|
||||
{
|
||||
panicThreshold,
|
||||
environment: {
|
||||
validateExhaustiveMemoizationDependencies: false,
|
||||
reactCompiler: {
|
||||
panicThreshold,
|
||||
environment: {
|
||||
validateExhaustiveMemoizationDependencies: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
@@ -282,7 +298,7 @@ describe("transformSync", () => {
|
||||
const table = useReactTable({});
|
||||
return <div>{table}</div>;
|
||||
}`,
|
||||
{ panicThreshold: "all_errors" },
|
||||
{ reactCompiler: { panicThreshold: "all_errors" } },
|
||||
);
|
||||
|
||||
expect(result.fatal).toBe(true);
|
||||
@@ -293,6 +309,61 @@ describe("transformSync", () => {
|
||||
message: "[ReactCompiler] IncompatibleLibrary: Use of incompatible library",
|
||||
});
|
||||
});
|
||||
|
||||
it("can disable and explicitly enable React Compiler", () => {
|
||||
const disabled = transformSync("Component.tsx", fixture, {
|
||||
reactCompiler: false,
|
||||
});
|
||||
expect(disabled.errors).toEqual([]);
|
||||
expect(disabled.code).not.toContain("react/compiler-runtime");
|
||||
expect(disabled.code).not.toContain("interface Props");
|
||||
expect(disabled.code).not.toContain("<button");
|
||||
|
||||
const enabled = transformSync("Component.tsx", fixture, {
|
||||
reactCompiler: true,
|
||||
});
|
||||
expect(enabled.errors).toEqual([]);
|
||||
expect(enabled.code).toContain("react/compiler-runtime");
|
||||
});
|
||||
|
||||
it("configures and preserves JSX independently of React Compiler", () => {
|
||||
const configured = transformSync("Component.tsx", fixture, {
|
||||
reactCompiler: false,
|
||||
jsx: { importSource: "custom-jsx" },
|
||||
});
|
||||
expect(configured.errors).toEqual([]);
|
||||
expect(configured.code).toContain('from "custom-jsx/jsx-runtime"');
|
||||
expect(configured.code).not.toContain("<button");
|
||||
|
||||
const preserved = transformSync("Component.tsx", fixture, {
|
||||
jsx: "preserve",
|
||||
});
|
||||
expect(preserved.errors).toEqual([]);
|
||||
expect(preserved.code).toContain("react/compiler-runtime");
|
||||
expect(preserved.code).not.toContain("interface Props");
|
||||
expect(preserved.code).toContain("<button");
|
||||
});
|
||||
|
||||
it("reports invalid JSX modes without emitting code", () => {
|
||||
const result = transformSync("Component.tsx", fixture, {
|
||||
reactCompiler: false,
|
||||
jsx: "invalid",
|
||||
} as never);
|
||||
expect(result.fatal).toBe(true);
|
||||
expect(result.code).toBe("");
|
||||
expect(result.errors).toHaveLength(1);
|
||||
expect(result.errors[0].message).toContain("Invalid `jsx` option");
|
||||
});
|
||||
|
||||
it("supports React Fast Refresh through JSX options", () => {
|
||||
const result = transformSync("Component.tsx", fixture, {
|
||||
reactCompiler: false,
|
||||
jsx: { refresh: true },
|
||||
});
|
||||
expect(result.errors).toEqual([]);
|
||||
expect(result.code).toContain("$RefreshSig$");
|
||||
expect(result.code).toContain("$RefreshReg$");
|
||||
});
|
||||
});
|
||||
|
||||
describe("transform", () => {
|
||||
|
||||
@@ -36,6 +36,76 @@ export interface SourceMap {
|
||||
version: number
|
||||
x_google_ignoreList?: Array<number>
|
||||
}
|
||||
/**
|
||||
* Configure how TSX and JSX are transformed.
|
||||
*
|
||||
* @see <https://oxc.rs/docs/guide/usage/transformer/jsx>
|
||||
*/
|
||||
export interface JsxOptions {
|
||||
/**
|
||||
* Decides which runtime to use.
|
||||
*
|
||||
* - 'automatic' - auto-import the correct JSX factories
|
||||
* - 'classic' - no auto-import
|
||||
*
|
||||
* @default 'automatic'
|
||||
*/
|
||||
runtime?: 'classic' | 'automatic'
|
||||
/**
|
||||
* Emit development-specific information, such as `__source` and `__self`.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
development?: boolean
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
throwIfNamespace?: boolean
|
||||
/**
|
||||
* Mark JSX elements and top-level React method calls as pure for tree shaking.
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
pure?: boolean
|
||||
/**
|
||||
* Replaces the import source when importing functions.
|
||||
*
|
||||
* @default 'react'
|
||||
*/
|
||||
importSource?: 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'
|
||||
*/
|
||||
pragma?: 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'
|
||||
*/
|
||||
pragmaFrag?: string
|
||||
/**
|
||||
* Enable React Fast Refresh.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
refresh?: boolean | ReactRefreshOptions
|
||||
}
|
||||
|
||||
/** Dynamic feature-gating import. */
|
||||
export interface ReactCompilerDynamicGating {
|
||||
source: string
|
||||
@@ -97,31 +167,11 @@ export interface ReactCompilerMetaTarget {
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a JavaScript or TypeScript React module asynchronously.
|
||||
* React Compiler options.
|
||||
*
|
||||
* This uses a worker-pool thread and can be slower than `transformSync` for a
|
||||
* single small module.
|
||||
* Fields mirror `babel-plugin-react-compiler` and `react-compiler-napi`.
|
||||
*/
|
||||
export declare function transform(filename: string, sourceText: string, options?: TransformOptions | undefined | null): Promise<TransformResult>
|
||||
|
||||
/**
|
||||
* Options for compiling a JavaScript or TypeScript React module.
|
||||
*
|
||||
* React Compiler fields mirror `babel-plugin-react-compiler` and
|
||||
* `react-compiler-napi`. `lang`, `sourceType`, and `sourcemap` configure the
|
||||
* surrounding Oxc parse/codegen pipeline.
|
||||
*/
|
||||
export interface TransformOptions {
|
||||
/** Treat the source as `js`, `jsx`, `ts`, `tsx`, or `dts`. */
|
||||
lang?: 'js' | 'jsx' | 'ts' | 'tsx' | 'dts'
|
||||
/** Treat the source as script, module, CommonJS, or infer it from syntax. */
|
||||
sourceType?: 'script' | 'module' | 'commonjs' | 'unambiguous'
|
||||
/**
|
||||
* Generate a source map.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
sourcemap?: boolean
|
||||
export interface ReactCompilerOptions {
|
||||
/**
|
||||
* Which functions the compiler attempts to compile.
|
||||
*
|
||||
@@ -184,6 +234,68 @@ export interface TransformOptions {
|
||||
environment?: ReactCompilerEnvironmentOptions
|
||||
}
|
||||
|
||||
/** React Fast Refresh options. */
|
||||
export interface ReactRefreshOptions {
|
||||
/**
|
||||
* Specify the identifier of the refresh registration variable.
|
||||
*
|
||||
* @default `$RefreshReg$`
|
||||
*/
|
||||
refreshReg?: string
|
||||
/**
|
||||
* Specify the identifier of the refresh signature variable.
|
||||
*
|
||||
* @default `$RefreshSig$`
|
||||
*/
|
||||
refreshSig?: string
|
||||
/**
|
||||
* Emit full hook signatures instead of compact hashes.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
emitFullSignatures?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a JavaScript or TypeScript React module asynchronously.
|
||||
*
|
||||
* This uses a worker-pool thread and can be slower than `transformSync` for a
|
||||
* single small module.
|
||||
*/
|
||||
export declare function transform(filename: string, sourceText: string, options?: TransformOptions | undefined | null): Promise<TransformResult>
|
||||
|
||||
/**
|
||||
* Options for compiling a JavaScript or TypeScript React module.
|
||||
*
|
||||
* `lang`, `sourceType`, and `sourcemap` configure the surrounding Oxc
|
||||
* parse/codegen pipeline. React Compiler and JSX transforms are configured
|
||||
* independently.
|
||||
*/
|
||||
export interface TransformOptions {
|
||||
/** Treat the source as `js`, `jsx`, `ts`, `tsx`, or `dts`. */
|
||||
lang?: 'js' | 'jsx' | 'ts' | 'tsx' | 'dts'
|
||||
/** Treat the source as script, module, CommonJS, or infer it from syntax. */
|
||||
sourceType?: 'script' | 'module' | 'commonjs' | 'unambiguous'
|
||||
/**
|
||||
* Generate a source map.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
sourcemap?: boolean
|
||||
/**
|
||||
* Configure how TSX and JSX are transformed, or preserve JSX syntax.
|
||||
*
|
||||
* @see <https://oxc.rs/docs/guide/usage/transformer/jsx>
|
||||
*/
|
||||
jsx?: 'preserve' | JsxOptions
|
||||
/**
|
||||
* Configure React Compiler, or disable it with `false`.
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
reactCompiler?: boolean | ReactCompilerOptions
|
||||
}
|
||||
|
||||
/** Result returned by the React Compiler transform. */
|
||||
export interface TransformResult {
|
||||
/** Whether the transform was aborted without emitting code. */
|
||||
@@ -204,7 +316,7 @@ export interface TransformResult {
|
||||
/**
|
||||
* Compile a JavaScript or TypeScript React module synchronously.
|
||||
*
|
||||
* The React Compiler runs first on the pristine AST. TypeScript and JSX are
|
||||
* lowered afterwards, matching the transform pipeline used by `oxc-transform`.
|
||||
* The React Compiler runs first on the pristine AST. TypeScript syntax is
|
||||
* removed and configured JSX transforms run afterwards.
|
||||
*/
|
||||
export declare function transformSync(filename: string, sourceText: string, options?: TransformOptions | undefined | null): TransformResult
|
||||
Reference in new issue
Block a user