feat(napi): add React Compiler transform binding (#24934)

Adds the `oxc-transform-react` N-API binding and its public CI and publish workflows.

AI-assisted.
This commit is contained in:
Boshen committed 2026-07-29 09:08:58 +00:00
1 parent 1b57f783fb
commit 85d0f4dd1f
27 files changed
+2050 -15

No files matched your search

+1
View File
@@ -61,6 +61,7 @@ pnpm-lock.yaml
/crates/oxc_codegen @dunqing
/crates/oxc_napi @dunqing
/napi/transform @dunqing
/napi/transform-react @dunqing
/napi/minify @dunqing
/npm/runtime @dunqing
/tasks/transform_conformance @dunqing
+1
View File
@@ -37,6 +37,7 @@ A-transformer:
"crates/oxc_traverse/**",
"tasks/transform_conformance/**",
"napi/transform/**",
"napi/transform-react/**",
]
A-linter:
+6 -6
View File
@@ -198,7 +198,7 @@ jobs:
id: filter
with:
exclude: oxc_linter,oxc_language_server
paths: napi/parser/,napi/minify/,napi/transform/,npm/oxc-types/,npm/runtime/,tasks/e2e/,pnpm-lock.yaml,package.json
paths: napi/parser/,napi/minify/,napi/transform/,napi/transform-react/,npm/oxc-types/,npm/runtime/,tasks/e2e/,pnpm-lock.yaml,package.json
token: ${{ secrets.GITHUB_TOKEN }}
pr-number: ${{ github.event.pull_request.number }}
- uses: oxc-project/setup-node@4c588e9266bd930b6ddc34307df0659ed511d187 # v1.3.1
@@ -222,8 +222,8 @@ jobs:
RUN_RAW_RANGE_TESTS: "true"
RUN_RAW_TOKENS_TESTS: "true"
run: |
pnpm --workspace-concurrency=1 --filter "./napi/parser" --filter "./napi/minify" --filter "./napi/transform" build-test
pnpm --workspace-concurrency=1 --filter "./napi/parser" --filter "./napi/minify" --filter "./napi/transform" test
pnpm --workspace-concurrency=1 --filter "./napi/parser" --filter "./napi/minify" --filter "./napi/transform" --filter "./napi/transform-react" build-test
pnpm --workspace-concurrency=1 --filter "./napi/parser" --filter "./napi/minify" --filter "./napi/transform" --filter "./napi/transform-react" test
- if: steps.filter.outputs.changed == 'true'
name: Run e2e tests
working-directory: tasks/e2e
@@ -343,7 +343,7 @@ jobs:
id: filter
with:
exclude: oxc_linter,oxc_language_server
paths: napi/parser/,napi/minify/,napi/transform/,npm/oxc-types/,npm/runtime/,tasks/e2e/,pnpm-lock.yaml,package.json
paths: napi/parser/,napi/minify/,napi/transform/,napi/transform-react/,npm/oxc-types/,npm/runtime/,tasks/e2e/,pnpm-lock.yaml,package.json
token: ${{ secrets.GITHUB_TOKEN }}
pr-number: ${{ github.event.pull_request.number }}
- uses: oxc-project/setup-node@4c588e9266bd930b6ddc34307df0659ed511d187 # v1.3.1
@@ -374,8 +374,8 @@ jobs:
env:
RUN_RAW_RANGE_TESTS: "true"
run: |
pnpm --workspace-concurrency=1 --filter "./napi/parser" --filter "./napi/minify" --filter "./napi/transform" build-test
pnpm --workspace-concurrency=1 --filter "./napi/parser" --filter "./napi/minify" --filter "./napi/transform" test
pnpm --workspace-concurrency=1 --filter "./napi/parser" --filter "./napi/minify" --filter "./napi/transform" --filter "./napi/transform-react" build-test
pnpm --workspace-concurrency=1 --filter "./napi/parser" --filter "./napi/minify" --filter "./napi/transform" --filter "./napi/transform-react" test
- if: steps.filter.outputs.changed == 'true'
name: Run e2e tests
working-directory: ${{ env.DEV_DRIVE_WORKSPACE }}/tasks/e2e
@@ -0,0 +1,26 @@
name: Release NAPI React Transform
permissions: {}
on:
push:
branches:
- main
paths:
- napi/transform-react/package.json
- .github/workflows/release_napi_transform_react.yml
- .github/workflows/reusable_release_napi.yml
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
release:
if: github.repository == 'oxc-project/oxc'
name: Release NAPI React Transform
uses: ./.github/workflows/reusable_release_napi.yml
with:
name: transform-react
permissions:
id-token: write # for `pnpm publish --provenance`
Generated
+14
View File
@@ -2718,6 +2718,20 @@ dependencies = [
"rustc-hash",
]
[[package]]
name = "oxc_transform_react_napi"
version = "0.142.0"
dependencies = [
"mimalloc-safe",
"napi",
"napi-build",
"napi-derive",
"oxc",
"oxc_napi",
"oxc_react_compiler",
"oxc_sourcemap",
]
[[package]]
name = "oxc_transformer"
version = "0.142.0"
+2 -1
View File
@@ -151,6 +151,7 @@ oxc_span = { version = "0.142.0", path = "crates/oxc_span" } # Source positions
oxc_str = { version = "0.142.0", path = "crates/oxc_str" } # String types
oxc_syntax = { version = "0.142.0", path = "crates/oxc_syntax" } # Syntax utilities
oxc_transform_napi = { version = "0.142.0", path = "napi/transform" } # Node.js transformer binding
oxc_transform_react_napi = { version = "0.142.0", path = "napi/transform-react" } # Node.js React Compiler binding
oxc_transformer = { version = "0.142.0", path = "crates/oxc_transformer" } # Code transformation
oxc_transformer_plugins = { version = "0.142.0", path = "crates/oxc_transformer_plugins" } # Transformer plugins
oxc_traverse = { version = "0.142.0", path = "crates/oxc_traverse" } # AST traversal
@@ -276,7 +277,7 @@ walkdir = "2.5.0" # Directory traversal
windows-sys = { version = "0.61.2" } # Windows API bindings
[workspace.metadata.cargo-shear]
ignored = ["oxc_transform_napi", "oxc_parser_napi", "oxc_minify_napi"]
ignored = ["oxc_transform_napi", "oxc_transform_react_napi", "oxc_parser_napi", "oxc_minify_napi"]
# `oxc_allocator` is both dogfooded here (workspace path) and published to crates.io.
# External workspace-dependencies (e.g. `oxc-css-parser`) pull it from the registry,
+3
View File
@@ -0,0 +1,3 @@
# Changelog
All notable changes to this package will be documented in this file.
+54
View File
@@ -0,0 +1,54 @@
[package]
name = "oxc_transform_react_napi"
version = "0.142.0"
authors.workspace = true
categories.workspace = true
edition.workspace = true
homepage.workspace = true
include = ["/src", "build.rs"]
keywords.workspace = true
license.workspace = true
publish = true
repository.workspace = true
rust-version.workspace = true
description.workspace = true
[lints]
workspace = true
[lib]
crate-type = ["cdylib", "lib"]
test = false
doctest = false
[dependencies]
oxc = { workspace = true, features = ["full", "react_compiler"] }
oxc_napi = { workspace = true }
oxc_react_compiler = { workspace = true }
oxc_sourcemap = { workspace = true, features = ["napi"] }
napi = { workspace = true }
napi-derive = { workspace = true }
[target.'cfg(target_os = "macos")'.dependencies]
mimalloc-safe = { workspace = true, optional = true, features = ["skip_collect_on_exit"] }
[target.'cfg(all(target_os = "linux", not(target_arch = "arm"), not(target_arch = "aarch64")))'.dependencies]
mimalloc-safe = { workspace = true, optional = true, features = [
"skip_collect_on_exit",
"local_dynamic_tls",
] }
[target.'cfg(all(target_os = "linux", target_arch = "aarch64"))'.dependencies]
mimalloc-safe = { workspace = true, optional = true, features = [
"skip_collect_on_exit",
"local_dynamic_tls",
"no_opt_arch",
] }
[build-dependencies]
napi-build = { workspace = true }
[features]
default = []
allocator = ["dep:mimalloc-safe"]
+38
View File
@@ -0,0 +1,38 @@
# Oxc React Transform
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.
```javascript
import { transformSync } from "oxc-transform-react";
const result = transformSync(
"Component.tsx",
`
export function Component({ name }: { name: string }) {
return <div>Hello {name}</div>;
}
`,
{
target: "19",
},
);
if (result.errors.length > 0) {
console.error(result.errors);
}
console.log(result.code);
```
The React Compiler options use the same names as
`babel-plugin-react-compiler`/`react-compiler-napi`, including
`compilationMode`, `panicThreshold`, `target`, `gating`, `outputMode`,
suppression controls, and the supported `environment` flags.
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.
+1
View File
@@ -0,0 +1 @@
export * from '@oxc-transform-react/binding-wasm32-wasi'
+3
View File
@@ -0,0 +1,3 @@
fn main() {
napi_build::setup();
}
+205
View File
@@ -0,0 +1,205 @@
/* auto-generated by NAPI-RS */
/* eslint-disable */
export interface Comment {
type: 'Line' | 'Block'
value: string
start: number
end: number
}
export interface ErrorLabel {
message: string | null
start: number
end: number
}
export interface OxcError {
severity: Severity
message: string
labels: Array<ErrorLabel>
helpMessage: string | null
codeframe: string | null
}
export declare const enum Severity {
Error = 'Error',
Warning = 'Warning',
Advice = 'Advice'
}
export interface SourceMap {
file?: string
mappings: string
names: Array<string>
sourceRoot?: string
sources: Array<string>
sourcesContent?: Array<string>
version: number
x_google_ignoreList?: Array<number>
}
/** Dynamic feature-gating import. */
export interface ReactCompilerDynamicGating {
source: string
}
/**
* Partial React Compiler environment configuration.
*
* Unset fields retain compiler defaults. Callback-valued providers and the
* compiler's test-only panic switch are intentionally not exposed.
*/
export interface ReactCompilerEnvironmentOptions {
customMacros?: Array<string>
enableResetCacheOnSourceFileChanges?: boolean
enablePreserveExistingMemoizationGuarantees?: boolean
validatePreserveExistingMemoizationGuarantees?: boolean
validateExhaustiveMemoizationDependencies?: boolean
validateExhaustiveEffectDependencies?: 'off' | 'all' | 'missing-only' | 'extra-only'
enableOptionalDependencies?: boolean
enableNameAnonymousFunctions?: boolean
validateHooksUsage?: boolean
validateRefAccessDuringRender?: boolean
validateNoSetStateInRender?: boolean
enableUseKeyedState?: boolean
validateNoSetStateInEffects?: boolean
validateNoDerivedComputationsInEffects?: boolean
validateNoDerivedComputationsInEffectsExp?: boolean
validateNoJsxInTryStatements?: boolean
validateStaticComponents?: boolean
validateNoCapitalizedCalls?: Array<string>
validateBlocklistedImports?: Array<string>
validateSourceLocations?: boolean
validateNoImpureFunctionsInRender?: boolean
validateNoFreezingKnownMutableFunctions?: boolean
enableAssumeHooksFollowRulesOfReact?: boolean
enableTransitivelyFreezeFunctionExpressions?: boolean
enableFunctionOutlining?: boolean
enableJsxOutlining?: boolean
assertValidMutableRanges?: boolean
enableCustomTypeDefinitionForReanimated?: boolean
enableTreatRefLikeIdentifiersAsRefs?: boolean
enableTreatSetIdentifiersAsStateSetters?: boolean
validateNoVoidUseMemo?: boolean
enableAllowSetStateFromRefsInEffects?: boolean
enableVerboseNoSetStateInEffect?: boolean
enableForest?: boolean
}
/** Static feature-gating import. */
export interface ReactCompilerGating {
source: string
importSpecifierName: string
}
/** Meta-internal React runtime target. */
export interface ReactCompilerMetaTarget {
kind: 'donotuse_meta_internal'
runtimeModule?: string
}
/**
* 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.
*
* 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
/**
* Which functions the compiler attempts to compile.
*
* @default 'infer'
*/
compilationMode?: 'infer' | 'syntax' | 'annotation' | 'all'
/**
* When compiler diagnostics escalate into a hard failure.
*
* @default 'none'
*/
panicThreshold?: 'none' | 'critical_errors' | 'all_errors'
/**
* React runtime target. React 17 and 18 use `react-compiler-runtime`;
* React 19 uses `react/compiler-runtime`.
*
* @default '19'
*/
target?: '17' | '18' | '19' | ReactCompilerMetaTarget
/** Emit both compiled and original functions behind an imported feature gate. */
gating?: ReactCompilerGating
/** Enable `"use memo if(...)"` directive-driven gating. */
dynamicGating?: ReactCompilerDynamicGating
/**
* Analyze and report diagnostics without applying compiler output.
*
* @deprecated Prefer `outputMode: "lint"`.
* @default false
*/
noEmit?: boolean
/** Select client, SSR, or lint output. */
outputMode?: 'client' | 'ssr' | 'lint'
/** ESLint rule names whose suppressions opt a function out of compilation. */
eslintSuppressionRules?: Array<string>
/**
* Treat Flow suppression comments as opt-outs.
*
* @default true
*/
flowSuppressions?: boolean
/**
* Compile functions carrying `"use no memo"` or `"use no forget"`.
*
* @default false
*/
ignoreUseNoForget?: boolean
/** Additional directives that opt a function out of compilation. */
customOptOutDirectives?: Array<string>
/**
* Only run the React Compiler when the filename contains one of these strings.
*
* Function-valued `sources` filters from the Babel plugin are intentionally
* unsupported across the native boundary.
*/
sources?: Array<string>
/** Feature flags and validation settings for compiler passes. */
environment?: ReactCompilerEnvironmentOptions
}
/** Result returned by the React Compiler transform. */
export interface TransformResult {
/**
* Transformed JavaScript code.
*
* This is empty when parsing, semantic analysis, option validation, or the
* React Compiler reports a fatal error.
*/
code: string
/** Source map, populated when `sourcemap` is `true`. */
map?: SourceMap
/** Parse, semantic, React Compiler, and downstream transform diagnostics. */
errors: Array<OxcError>
}
/**
* 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`.
*/
export declare function transformSync(filename: string, sourceText: string, options?: TransformOptions | undefined | null): TransformResult
+604
View File
@@ -0,0 +1,604 @@
// prettier-ignore
/* eslint-disable */
// @ts-nocheck
/* auto-generated by NAPI-RS */
import { createRequire } from 'module'
const require = createRequire(import.meta.url)
const __dirname = new URL('.', import.meta.url).pathname
const { readFileSync } = require('fs')
let nativeBinding = null
const loadErrors = []
const isMusl = () => {
let musl = false
if (process.platform === 'linux') {
musl = isMuslFromFilesystem()
if (musl === null) {
musl = isMuslFromReport()
}
if (musl === null) {
musl = isMuslFromChildProcess()
}
}
return musl
}
const isFileMusl = (f) => f.includes('libc.musl-') || f.includes('ld-musl-')
const isMuslFromFilesystem = () => {
try {
return readFileSync('/usr/bin/ldd', 'utf-8').includes('musl')
} catch {
return null
}
}
const isMuslFromReport = () => {
let report = null
if (process.report && typeof process.report.getReport === 'function') {
process.report.excludeNetwork = true
report = process.report.getReport()
}
if (!report) {
return null
}
if (report.header && report.header.glibcVersionRuntime) {
return false
}
if (Array.isArray(report.sharedObjects)) {
if (report.sharedObjects.some(isFileMusl)) {
return true
}
}
return false
}
const isMuslFromChildProcess = () => {
try {
return require('child_process').execSync('ldd --version', { encoding: 'utf8' }).includes('musl')
} catch (e) {
// If we reach this case, we don't know if the system is musl or not, so is better to just fallback to false
return false
}
}
function requireNative() {
if (process.env.NAPI_RS_NATIVE_LIBRARY_PATH) {
try {
return require(process.env.NAPI_RS_NATIVE_LIBRARY_PATH);
} catch (err) {
loadErrors.push(err)
}
} else if (process.platform === 'android') {
if (process.arch === 'arm64') {
try {
return require('./transform-react.android-arm64.node')
} catch (e) {
loadErrors.push(e)
}
try {
const binding = require('@oxc-transform-react/binding-android-arm64')
const bindingPackageVersion = require('@oxc-transform-react/binding-android-arm64/package.json').version
if (bindingPackageVersion !== '0.142.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
throw new Error(`Native binding package version mismatch, expected 0.142.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
}
return binding
} catch (e) {
loadErrors.push(e)
}
} else if (process.arch === 'arm') {
try {
return require('./transform-react.android-arm-eabi.node')
} catch (e) {
loadErrors.push(e)
}
try {
const binding = require('@oxc-transform-react/binding-android-arm-eabi')
const bindingPackageVersion = require('@oxc-transform-react/binding-android-arm-eabi/package.json').version
if (bindingPackageVersion !== '0.142.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
throw new Error(`Native binding package version mismatch, expected 0.142.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
}
return binding
} catch (e) {
loadErrors.push(e)
}
} else {
loadErrors.push(new Error(`Unsupported architecture on Android ${process.arch}`))
}
} else if (process.platform === 'win32') {
if (process.arch === 'x64') {
if ((process.config && process.config.variables && process.config.variables.shlib_suffix === 'dll.a') || (process.config && process.config.variables && process.config.variables.node_target_type === 'shared_library')) {
try {
return require('./transform-react.win32-x64-gnu.node')
} catch (e) {
loadErrors.push(e)
}
try {
const binding = require('@oxc-transform-react/binding-win32-x64-gnu')
const bindingPackageVersion = require('@oxc-transform-react/binding-win32-x64-gnu/package.json').version
if (bindingPackageVersion !== '0.142.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
throw new Error(`Native binding package version mismatch, expected 0.142.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
}
return binding
} catch (e) {
loadErrors.push(e)
}
} else {
try {
return require('./transform-react.win32-x64-msvc.node')
} catch (e) {
loadErrors.push(e)
}
try {
const binding = require('@oxc-transform-react/binding-win32-x64-msvc')
const bindingPackageVersion = require('@oxc-transform-react/binding-win32-x64-msvc/package.json').version
if (bindingPackageVersion !== '0.142.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
throw new Error(`Native binding package version mismatch, expected 0.142.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
}
return binding
} catch (e) {
loadErrors.push(e)
}
}
} else if (process.arch === 'ia32') {
try {
return require('./transform-react.win32-ia32-msvc.node')
} catch (e) {
loadErrors.push(e)
}
try {
const binding = require('@oxc-transform-react/binding-win32-ia32-msvc')
const bindingPackageVersion = require('@oxc-transform-react/binding-win32-ia32-msvc/package.json').version
if (bindingPackageVersion !== '0.142.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
throw new Error(`Native binding package version mismatch, expected 0.142.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
}
return binding
} catch (e) {
loadErrors.push(e)
}
} else if (process.arch === 'arm64') {
try {
return require('./transform-react.win32-arm64-msvc.node')
} catch (e) {
loadErrors.push(e)
}
try {
const binding = require('@oxc-transform-react/binding-win32-arm64-msvc')
const bindingPackageVersion = require('@oxc-transform-react/binding-win32-arm64-msvc/package.json').version
if (bindingPackageVersion !== '0.142.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
throw new Error(`Native binding package version mismatch, expected 0.142.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
}
return binding
} catch (e) {
loadErrors.push(e)
}
} else {
loadErrors.push(new Error(`Unsupported architecture on Windows: ${process.arch}`))
}
} else if (process.platform === 'darwin') {
try {
return require('./transform-react.darwin-universal.node')
} catch (e) {
loadErrors.push(e)
}
try {
const binding = require('@oxc-transform-react/binding-darwin-universal')
const bindingPackageVersion = require('@oxc-transform-react/binding-darwin-universal/package.json').version
if (bindingPackageVersion !== '0.142.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
throw new Error(`Native binding package version mismatch, expected 0.142.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
}
return binding
} catch (e) {
loadErrors.push(e)
}
if (process.arch === 'x64') {
try {
return require('./transform-react.darwin-x64.node')
} catch (e) {
loadErrors.push(e)
}
try {
const binding = require('@oxc-transform-react/binding-darwin-x64')
const bindingPackageVersion = require('@oxc-transform-react/binding-darwin-x64/package.json').version
if (bindingPackageVersion !== '0.142.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
throw new Error(`Native binding package version mismatch, expected 0.142.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
}
return binding
} catch (e) {
loadErrors.push(e)
}
} else if (process.arch === 'arm64') {
try {
return require('./transform-react.darwin-arm64.node')
} catch (e) {
loadErrors.push(e)
}
try {
const binding = require('@oxc-transform-react/binding-darwin-arm64')
const bindingPackageVersion = require('@oxc-transform-react/binding-darwin-arm64/package.json').version
if (bindingPackageVersion !== '0.142.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
throw new Error(`Native binding package version mismatch, expected 0.142.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
}
return binding
} catch (e) {
loadErrors.push(e)
}
} else {
loadErrors.push(new Error(`Unsupported architecture on macOS: ${process.arch}`))
}
} else if (process.platform === 'freebsd') {
if (process.arch === 'x64') {
try {
return require('./transform-react.freebsd-x64.node')
} catch (e) {
loadErrors.push(e)
}
try {
const binding = require('@oxc-transform-react/binding-freebsd-x64')
const bindingPackageVersion = require('@oxc-transform-react/binding-freebsd-x64/package.json').version
if (bindingPackageVersion !== '0.142.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
throw new Error(`Native binding package version mismatch, expected 0.142.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
}
return binding
} catch (e) {
loadErrors.push(e)
}
} else if (process.arch === 'arm64') {
try {
return require('./transform-react.freebsd-arm64.node')
} catch (e) {
loadErrors.push(e)
}
try {
const binding = require('@oxc-transform-react/binding-freebsd-arm64')
const bindingPackageVersion = require('@oxc-transform-react/binding-freebsd-arm64/package.json').version
if (bindingPackageVersion !== '0.142.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
throw new Error(`Native binding package version mismatch, expected 0.142.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
}
return binding
} catch (e) {
loadErrors.push(e)
}
} else {
loadErrors.push(new Error(`Unsupported architecture on FreeBSD: ${process.arch}`))
}
} else if (process.platform === 'linux') {
if (process.arch === 'x64') {
if (isMusl()) {
try {
return require('./transform-react.linux-x64-musl.node')
} catch (e) {
loadErrors.push(e)
}
try {
const binding = require('@oxc-transform-react/binding-linux-x64-musl')
const bindingPackageVersion = require('@oxc-transform-react/binding-linux-x64-musl/package.json').version
if (bindingPackageVersion !== '0.142.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
throw new Error(`Native binding package version mismatch, expected 0.142.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
}
return binding
} catch (e) {
loadErrors.push(e)
}
} else {
try {
return require('./transform-react.linux-x64-gnu.node')
} catch (e) {
loadErrors.push(e)
}
try {
const binding = require('@oxc-transform-react/binding-linux-x64-gnu')
const bindingPackageVersion = require('@oxc-transform-react/binding-linux-x64-gnu/package.json').version
if (bindingPackageVersion !== '0.142.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
throw new Error(`Native binding package version mismatch, expected 0.142.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
}
return binding
} catch (e) {
loadErrors.push(e)
}
}
} else if (process.arch === 'arm64') {
if (isMusl()) {
try {
return require('./transform-react.linux-arm64-musl.node')
} catch (e) {
loadErrors.push(e)
}
try {
const binding = require('@oxc-transform-react/binding-linux-arm64-musl')
const bindingPackageVersion = require('@oxc-transform-react/binding-linux-arm64-musl/package.json').version
if (bindingPackageVersion !== '0.142.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
throw new Error(`Native binding package version mismatch, expected 0.142.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
}
return binding
} catch (e) {
loadErrors.push(e)
}
} else {
try {
return require('./transform-react.linux-arm64-gnu.node')
} catch (e) {
loadErrors.push(e)
}
try {
const binding = require('@oxc-transform-react/binding-linux-arm64-gnu')
const bindingPackageVersion = require('@oxc-transform-react/binding-linux-arm64-gnu/package.json').version
if (bindingPackageVersion !== '0.142.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
throw new Error(`Native binding package version mismatch, expected 0.142.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
}
return binding
} catch (e) {
loadErrors.push(e)
}
}
} else if (process.arch === 'arm') {
if (isMusl()) {
try {
return require('./transform-react.linux-arm-musleabihf.node')
} catch (e) {
loadErrors.push(e)
}
try {
const binding = require('@oxc-transform-react/binding-linux-arm-musleabihf')
const bindingPackageVersion = require('@oxc-transform-react/binding-linux-arm-musleabihf/package.json').version
if (bindingPackageVersion !== '0.142.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
throw new Error(`Native binding package version mismatch, expected 0.142.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
}
return binding
} catch (e) {
loadErrors.push(e)
}
} else {
try {
return require('./transform-react.linux-arm-gnueabihf.node')
} catch (e) {
loadErrors.push(e)
}
try {
const binding = require('@oxc-transform-react/binding-linux-arm-gnueabihf')
const bindingPackageVersion = require('@oxc-transform-react/binding-linux-arm-gnueabihf/package.json').version
if (bindingPackageVersion !== '0.142.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
throw new Error(`Native binding package version mismatch, expected 0.142.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
}
return binding
} catch (e) {
loadErrors.push(e)
}
}
} else if (process.arch === 'loong64') {
if (isMusl()) {
try {
return require('./transform-react.linux-loong64-musl.node')
} catch (e) {
loadErrors.push(e)
}
try {
const binding = require('@oxc-transform-react/binding-linux-loong64-musl')
const bindingPackageVersion = require('@oxc-transform-react/binding-linux-loong64-musl/package.json').version
if (bindingPackageVersion !== '0.142.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
throw new Error(`Native binding package version mismatch, expected 0.142.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
}
return binding
} catch (e) {
loadErrors.push(e)
}
} else {
try {
return require('./transform-react.linux-loong64-gnu.node')
} catch (e) {
loadErrors.push(e)
}
try {
const binding = require('@oxc-transform-react/binding-linux-loong64-gnu')
const bindingPackageVersion = require('@oxc-transform-react/binding-linux-loong64-gnu/package.json').version
if (bindingPackageVersion !== '0.142.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
throw new Error(`Native binding package version mismatch, expected 0.142.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
}
return binding
} catch (e) {
loadErrors.push(e)
}
}
} else if (process.arch === 'riscv64') {
if (isMusl()) {
try {
return require('./transform-react.linux-riscv64-musl.node')
} catch (e) {
loadErrors.push(e)
}
try {
const binding = require('@oxc-transform-react/binding-linux-riscv64-musl')
const bindingPackageVersion = require('@oxc-transform-react/binding-linux-riscv64-musl/package.json').version
if (bindingPackageVersion !== '0.142.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
throw new Error(`Native binding package version mismatch, expected 0.142.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
}
return binding
} catch (e) {
loadErrors.push(e)
}
} else {
try {
return require('./transform-react.linux-riscv64-gnu.node')
} catch (e) {
loadErrors.push(e)
}
try {
const binding = require('@oxc-transform-react/binding-linux-riscv64-gnu')
const bindingPackageVersion = require('@oxc-transform-react/binding-linux-riscv64-gnu/package.json').version
if (bindingPackageVersion !== '0.142.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
throw new Error(`Native binding package version mismatch, expected 0.142.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
}
return binding
} catch (e) {
loadErrors.push(e)
}
}
} else if (process.arch === 'ppc64') {
try {
return require('./transform-react.linux-ppc64-gnu.node')
} catch (e) {
loadErrors.push(e)
}
try {
const binding = require('@oxc-transform-react/binding-linux-ppc64-gnu')
const bindingPackageVersion = require('@oxc-transform-react/binding-linux-ppc64-gnu/package.json').version
if (bindingPackageVersion !== '0.142.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
throw new Error(`Native binding package version mismatch, expected 0.142.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
}
return binding
} catch (e) {
loadErrors.push(e)
}
} else if (process.arch === 's390x') {
try {
return require('./transform-react.linux-s390x-gnu.node')
} catch (e) {
loadErrors.push(e)
}
try {
const binding = require('@oxc-transform-react/binding-linux-s390x-gnu')
const bindingPackageVersion = require('@oxc-transform-react/binding-linux-s390x-gnu/package.json').version
if (bindingPackageVersion !== '0.142.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
throw new Error(`Native binding package version mismatch, expected 0.142.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
}
return binding
} catch (e) {
loadErrors.push(e)
}
} else {
loadErrors.push(new Error(`Unsupported architecture on Linux: ${process.arch}`))
}
} else if (process.platform === 'openharmony') {
if (process.arch === 'arm64') {
try {
return require('./transform-react.openharmony-arm64.node')
} catch (e) {
loadErrors.push(e)
}
try {
const binding = require('@oxc-transform-react/binding-openharmony-arm64')
const bindingPackageVersion = require('@oxc-transform-react/binding-openharmony-arm64/package.json').version
if (bindingPackageVersion !== '0.142.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
throw new Error(`Native binding package version mismatch, expected 0.142.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
}
return binding
} catch (e) {
loadErrors.push(e)
}
} else if (process.arch === 'x64') {
try {
return require('./transform-react.openharmony-x64.node')
} catch (e) {
loadErrors.push(e)
}
try {
const binding = require('@oxc-transform-react/binding-openharmony-x64')
const bindingPackageVersion = require('@oxc-transform-react/binding-openharmony-x64/package.json').version
if (bindingPackageVersion !== '0.142.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
throw new Error(`Native binding package version mismatch, expected 0.142.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
}
return binding
} catch (e) {
loadErrors.push(e)
}
} else if (process.arch === 'arm') {
try {
return require('./transform-react.openharmony-arm.node')
} catch (e) {
loadErrors.push(e)
}
try {
const binding = require('@oxc-transform-react/binding-openharmony-arm')
const bindingPackageVersion = require('@oxc-transform-react/binding-openharmony-arm/package.json').version
if (bindingPackageVersion !== '0.142.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
throw new Error(`Native binding package version mismatch, expected 0.142.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
}
return binding
} catch (e) {
loadErrors.push(e)
}
} else {
loadErrors.push(new Error(`Unsupported architecture on OpenHarmony: ${process.arch}`))
}
} else {
loadErrors.push(new Error(`Unsupported OS: ${process.platform}, architecture: ${process.arch}`))
}
}
nativeBinding = requireNative()
// NAPI_RS_FORCE_WASI is a tri-state flag:
// unset / any other value → native binding preferred, WASI is only a fallback
// 'true' → force WASI fallback even if native loaded
// 'error' → force WASI and throw if no WASI binding is found
// Treating any non-empty string as truthy (the historical behavior) meant
// NAPI_RS_FORCE_WASI=false, NAPI_RS_FORCE_WASI=0, etc. inadvertently triggered
// the WASI path, causing ENOENT for packages shipped without a .wasi.cjs file.
const forceWasi =
process.env.NAPI_RS_FORCE_WASI === 'true' || process.env.NAPI_RS_FORCE_WASI === 'error'
if (!nativeBinding || forceWasi) {
let wasiBinding = null
let wasiBindingError = null
try {
wasiBinding = require('./transform-react.wasi.cjs')
nativeBinding = wasiBinding
} catch (err) {
if (forceWasi) {
wasiBindingError = err
}
}
if (!nativeBinding || forceWasi) {
try {
wasiBinding = require('@oxc-transform-react/binding-wasm32-wasi')
nativeBinding = wasiBinding
} catch (err) {
if (forceWasi) {
if (!wasiBindingError) {
wasiBindingError = err
} else {
wasiBindingError.cause = err
}
loadErrors.push(err)
}
}
}
if (process.env.NAPI_RS_FORCE_WASI === 'error' && !wasiBinding) {
const error = new Error('WASI binding not found and NAPI_RS_FORCE_WASI is set to error')
error.cause = wasiBindingError
throw error
}
}
if (!nativeBinding && globalThis.process?.versions?.["webcontainer"]) {
try {
nativeBinding = require('./webcontainer-fallback.cjs');
} catch (err) {
loadErrors.push(err)
}
}
if (!nativeBinding) {
if (loadErrors.length > 0) {
const error = new Error(
`Cannot find native binding. ` +
`npm has a bug related to optional dependencies (https://github.com/npm/cli/issues/4828). ` +
'Please try `npm i` again after removing both package-lock.json and node_modules directory.',
)
// assign instead of the `new Error(message, { cause })` options form,
// which Node < 16.9 silently ignores
error.cause = loadErrors.reduce((err, cur) => {
cur.cause = err
return cur
})
throw error
}
throw new Error(`Failed to load native binding`)
}
const { Severity, transform, transformSync } = nativeBinding
export { Severity }
export { transform }
export { transformSync }
+93
View File
@@ -0,0 +1,93 @@
{
"name": "oxc-transform-react",
"version": "0.142.0",
"description": "Oxc React Compiler Node API",
"keywords": [
"compiler",
"javascript",
"jsx",
"oxc",
"react",
"transform",
"tsx",
"typescript"
],
"homepage": "https://oxc.rs",
"bugs": "https://github.com/oxc-project/oxc/issues",
"license": "MIT",
"author": "Boshen and oxc contributors",
"repository": {
"type": "git",
"url": "git+https://github.com/oxc-project/oxc.git",
"directory": "napi/transform-react"
},
"funding": {
"url": "https://github.com/sponsors/Boshen"
},
"files": [
"browser.js",
"index.d.ts",
"index.js",
"webcontainer-fallback.cjs"
],
"type": "module",
"sideEffects": false,
"main": "index.js",
"browser": "browser.js",
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org/"
},
"scripts": {
"build-dev": "napi build --esm --platform",
"build-test": "pnpm run build-dev --profile coverage",
"build": "pnpm run build-dev --features allocator --release",
"postbuild": "publint",
"postbuild-dev": "node scripts/patch.js",
"build-wasm": "pnpm run build-wasm-dev --release",
"build-wasm-dev": "pnpm run build-dev --target wasm32-wasip1-threads",
"test": "vitest run --dir ./test"
},
"devDependencies": {
"@emnapi/core": "catalog:",
"@emnapi/runtime": "catalog:",
"@napi-rs/cli": "catalog:",
"@types/node": "catalog:",
"publint": "catalog:",
"vitest": "catalog:"
},
"napi": {
"binaryName": "transform-react",
"packageName": "@oxc-transform-react/binding",
"targets": [
"aarch64-apple-darwin",
"aarch64-linux-android",
"aarch64-pc-windows-msvc",
"aarch64-unknown-linux-gnu",
"aarch64-unknown-linux-musl",
"aarch64-unknown-linux-ohos",
"armv7-linux-androideabi",
"armv7-unknown-linux-gnueabihf",
"armv7-unknown-linux-musleabihf",
"i686-pc-windows-msvc",
"powerpc64le-unknown-linux-gnu",
"riscv64gc-unknown-linux-gnu",
"riscv64gc-unknown-linux-musl",
"s390x-unknown-linux-gnu",
"wasm32-wasip1-threads",
"x86_64-apple-darwin",
"x86_64-pc-windows-msvc",
"x86_64-unknown-freebsd",
"x86_64-unknown-linux-gnu",
"x86_64-unknown-linux-musl"
],
"wasm": {
"browser": {
"fs": false
}
}
},
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
}
+25
View File
@@ -0,0 +1,25 @@
import fs from "node:fs";
import { join as pathJoin } from "node:path";
const packageDir = pathJoin(import.meta.dirname, "..");
const path = pathJoin(packageDir, "index.js");
let data = fs.readFileSync(path, "utf-8");
data = data.replace(
"\nif (!nativeBinding) {",
(source) =>
`
if (!nativeBinding && globalThis.process?.versions?.["webcontainer"]) {
try {
nativeBinding = require('./webcontainer-fallback.cjs');
} catch (err) {
loadErrors.push(err)
}
}
` + source,
);
fs.writeFileSync(path, data);
const workerPath = pathJoin(packageDir, "wasi-worker-browser.mjs");
const worker = fs.readFileSync(workerPath, "utf-8").replaceAll(/[ \t]+$/gmu, "");
fs.writeFileSync(workerPath, worker);
+167
View File
@@ -0,0 +1,167 @@
#![expect(clippy::needless_pass_by_value)]
#[cfg(all(
feature = "allocator",
not(any(
target_arch = "arm",
target_os = "android",
target_os = "freebsd",
target_os = "windows",
target_family = "wasm"
))
))]
#[global_allocator]
static ALLOC: mimalloc_safe::MiMalloc = mimalloc_safe::MiMalloc;
mod options;
use std::path::Path;
use napi::{Task, bindgen_prelude::AsyncTask};
use napi_derive::napi;
use oxc::{
CompilerInterface,
codegen::CodegenReturn,
diagnostics::{Diagnostics, OxcDiagnostic},
};
use oxc_napi::{OxcError, get_source_type};
use oxc_sourcemap::napi::SourceMap;
pub use crate::options::*;
/// Result returned by the React Compiler transform.
#[derive(Default)]
#[napi(object)]
pub struct TransformResult {
/// Transformed JavaScript code.
///
/// This is empty when parsing, semantic analysis, option validation, or the
/// React Compiler reports a fatal error.
pub code: String,
/// Source map, populated when `sourcemap` is `true`.
pub map: Option<SourceMap>,
/// Parse, semantic, React Compiler, and downstream transform diagnostics.
pub errors: Vec<OxcError>,
}
#[derive(Default)]
struct Compiler {
transform_options: oxc::transformer::TransformOptions,
sourcemap: bool,
printed: String,
printed_sourcemap: Option<SourceMap>,
errors: Diagnostics,
}
impl Compiler {
fn new(filename: &str, options: Option<TransformOptions>) -> Result<Self, OxcDiagnostic> {
let sourcemap = options.as_ref().and_then(|options| options.sourcemap).unwrap_or(false);
let transform_options = options.unwrap_or_default().into_transform_options(filename)?;
Ok(Self {
transform_options,
sourcemap,
printed: String::new(),
printed_sourcemap: None,
errors: Diagnostics::new(),
})
}
}
impl CompilerInterface for Compiler {
fn handle_errors(&mut self, errors: Diagnostics) {
self.errors.extend(errors);
}
fn enable_sourcemap(&self) -> bool {
self.sourcemap
}
fn transform_options(&self) -> Option<&oxc::transformer::TransformOptions> {
Some(&self.transform_options)
}
fn after_codegen(&mut self, ret: CodegenReturn<'_>) {
self.printed = ret.code;
self.printed_sourcemap = ret.map.map(SourceMap::from);
}
}
fn transform_impl(
filename: &str,
source_text: &str,
options: Option<TransformOptions>,
) -> TransformResult {
let source_type = get_source_type(
filename,
options.as_ref().and_then(|options| options.lang.as_deref()),
options.as_ref().and_then(|options| options.source_type.as_deref()),
);
let mut compiler = match Compiler::new(filename, options) {
Ok(compiler) => compiler,
Err(error) => {
return TransformResult {
errors: OxcError::from_diagnostics(filename, source_text, [error]),
..TransformResult::default()
};
}
};
compiler.compile(source_text, source_type, Path::new(filename));
TransformResult {
code: compiler.printed,
map: compiler.printed_sourcemap,
errors: OxcError::from_diagnostics(filename, source_text, compiler.errors),
}
}
/// 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`.
#[napi]
pub fn transform_sync(
filename: String,
source_text: String,
options: Option<TransformOptions>,
) -> TransformResult {
transform_impl(&filename, &source_text, options)
}
pub struct TransformTask {
filename: String,
source_text: String,
options: Option<TransformOptions>,
}
#[napi]
impl Task for TransformTask {
type JsValue = TransformResult;
type Output = TransformResult;
fn compute(&mut self) -> napi::Result<Self::Output> {
Ok(transform_impl(&self.filename, &self.source_text, self.options.take()))
}
fn resolve(&mut self, _: napi::Env, result: Self::Output) -> napi::Result<Self::JsValue> {
Ok(result)
}
}
/// 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.
#[napi]
pub fn transform(
filename: String,
source_text: String,
options: Option<TransformOptions>,
) -> AsyncTask<TransformTask> {
AsyncTask::new(TransformTask { filename, source_text, options })
}
+327
View File
@@ -0,0 +1,327 @@
use std::str::FromStr;
use napi::Either;
use napi_derive::napi;
use oxc::diagnostics::OxcDiagnostic;
use oxc_react_compiler::{
CompilerTarget, DynamicGatingConfig, EnvironmentConfig, ExhaustiveEffectDepsMode, GatingConfig,
PluginOptions,
};
/// 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.
#[napi(object)]
#[derive(Default, Debug)]
pub struct TransformOptions {
/// Treat the source as `js`, `jsx`, `ts`, `tsx`, or `dts`.
#[napi(ts_type = "'js' | 'jsx' | 'ts' | 'tsx' | 'dts'")]
pub lang: Option<String>,
/// Treat the source as script, module, CommonJS, or infer it from syntax.
#[napi(ts_type = "'script' | 'module' | 'commonjs' | 'unambiguous'")]
pub source_type: Option<String>,
/// Generate a source map.
///
/// @default false
pub sourcemap: Option<bool>,
/// Which functions the compiler attempts to compile.
///
/// @default 'infer'
#[napi(ts_type = "'infer' | 'syntax' | 'annotation' | 'all'")]
pub compilation_mode: Option<String>,
/// When compiler diagnostics escalate into a hard failure.
///
/// @default 'none'
#[napi(ts_type = "'none' | 'critical_errors' | 'all_errors'")]
pub panic_threshold: Option<String>,
/// React runtime target. React 17 and 18 use `react-compiler-runtime`;
/// React 19 uses `react/compiler-runtime`.
///
/// @default '19'
#[napi(ts_type = "'17' | '18' | '19' | ReactCompilerMetaTarget")]
pub target: Option<Either<String, ReactCompilerMetaTarget>>,
/// Emit both compiled and original functions behind an imported feature gate.
pub gating: Option<ReactCompilerGating>,
/// Enable `"use memo if(...)"` directive-driven gating.
pub dynamic_gating: Option<ReactCompilerDynamicGating>,
/// Analyze and report diagnostics without applying compiler output.
///
/// @deprecated Prefer `outputMode: "lint"`.
/// @default false
pub no_emit: Option<bool>,
/// Select client, SSR, or lint output.
#[napi(ts_type = "'client' | 'ssr' | 'lint'")]
pub output_mode: Option<String>,
/// ESLint rule names whose suppressions opt a function out of compilation.
pub eslint_suppression_rules: Option<Vec<String>>,
/// Treat Flow suppression comments as opt-outs.
///
/// @default true
pub flow_suppressions: Option<bool>,
/// Compile functions carrying `"use no memo"` or `"use no forget"`.
///
/// @default false
pub ignore_use_no_forget: Option<bool>,
/// Additional directives that opt a function out of compilation.
pub custom_opt_out_directives: Option<Vec<String>>,
/// Only run the React Compiler when the filename contains one of these strings.
///
/// Function-valued `sources` filters from the Babel plugin are intentionally
/// unsupported across the native boundary.
pub sources: Option<Vec<String>>,
/// Feature flags and validation settings for compiler passes.
pub environment: Option<ReactCompilerEnvironmentOptions>,
}
/// Meta-internal React runtime target.
#[napi(object)]
#[derive(Debug)]
pub struct ReactCompilerMetaTarget {
#[napi(ts_type = "'donotuse_meta_internal'")]
pub kind: String,
pub runtime_module: Option<String>,
}
/// Static feature-gating import.
#[napi(object)]
#[derive(Debug)]
pub struct ReactCompilerGating {
pub source: String,
pub import_specifier_name: String,
}
/// Dynamic feature-gating import.
#[napi(object)]
#[derive(Debug)]
pub struct ReactCompilerDynamicGating {
pub source: String,
}
/// Partial React Compiler environment configuration.
///
/// Unset fields retain compiler defaults. Callback-valued providers and the
/// compiler's test-only panic switch are intentionally not exposed.
#[napi(object)]
#[derive(Default, Debug)]
pub struct ReactCompilerEnvironmentOptions {
pub custom_macros: Option<Vec<String>>,
pub enable_reset_cache_on_source_file_changes: Option<bool>,
pub enable_preserve_existing_memoization_guarantees: Option<bool>,
pub validate_preserve_existing_memoization_guarantees: Option<bool>,
pub validate_exhaustive_memoization_dependencies: Option<bool>,
#[napi(ts_type = "'off' | 'all' | 'missing-only' | 'extra-only'")]
pub validate_exhaustive_effect_dependencies: Option<String>,
pub enable_optional_dependencies: Option<bool>,
pub enable_name_anonymous_functions: Option<bool>,
pub validate_hooks_usage: Option<bool>,
pub validate_ref_access_during_render: Option<bool>,
pub validate_no_set_state_in_render: Option<bool>,
pub enable_use_keyed_state: Option<bool>,
pub validate_no_set_state_in_effects: Option<bool>,
pub validate_no_derived_computations_in_effects: Option<bool>,
pub validate_no_derived_computations_in_effects_exp: Option<bool>,
pub validate_no_jsx_in_try_statements: Option<bool>,
pub validate_static_components: Option<bool>,
pub validate_no_capitalized_calls: Option<Vec<String>>,
pub validate_blocklisted_imports: Option<Vec<String>>,
pub validate_source_locations: Option<bool>,
pub validate_no_impure_functions_in_render: Option<bool>,
pub validate_no_freezing_known_mutable_functions: Option<bool>,
pub enable_assume_hooks_follow_rules_of_react: Option<bool>,
pub enable_transitively_freeze_function_expressions: Option<bool>,
pub enable_function_outlining: Option<bool>,
pub enable_jsx_outlining: Option<bool>,
pub assert_valid_mutable_ranges: Option<bool>,
pub enable_custom_type_definition_for_reanimated: Option<bool>,
pub enable_treat_ref_like_identifiers_as_refs: Option<bool>,
pub enable_treat_set_identifiers_as_state_setters: Option<bool>,
pub validate_no_void_use_memo: Option<bool>,
pub enable_allow_set_state_from_refs_in_effects: Option<bool>,
pub enable_verbose_no_set_state_in_effect: Option<bool>,
pub enable_forest: Option<bool>,
}
impl TransformOptions {
pub(crate) fn into_transform_options(
self,
filename: &str,
) -> Result<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()?;
Ok(oxc::transformer::TransformOptions {
react_compiler,
jsx: oxc::transformer::JsxOptions::enable(),
..oxc::transformer::TransformOptions::default()
})
}
fn into_plugin_options(self) -> Result<PluginOptions, OxcDiagnostic> {
let mut options = PluginOptions::default();
if let Some(value) = self.compilation_mode {
options.compilation_mode = parse(&value, "compilationMode")?;
}
if let Some(value) = self.panic_threshold {
options.panic_threshold = parse(&value, "panicThreshold")?;
}
if let Some(target) = self.target {
options.target = match target {
Either::A(version) => {
if !matches!(version.as_str(), "17" | "18" | "19") {
return Err(invalid_option("target", &version));
}
CompilerTarget::Version(version)
}
Either::B(target) => {
if target.kind != "donotuse_meta_internal" {
return Err(invalid_option("target.kind", &target.kind));
}
CompilerTarget::MetaInternal {
kind: target.kind,
runtime_module: target
.runtime_module
.unwrap_or_else(|| "react".to_string()),
}
}
};
}
if let Some(value) = self.no_emit {
options.no_emit = value;
}
if let Some(value) = self.output_mode {
options.output_mode = Some(parse(&value, "outputMode")?);
}
if let Some(value) = self.flow_suppressions {
options.flow_suppressions = value;
}
if let Some(value) = self.ignore_use_no_forget {
options.ignore_use_no_forget = value;
}
if self.eslint_suppression_rules.is_some() {
options.eslint_suppression_rules = self.eslint_suppression_rules;
}
if self.custom_opt_out_directives.is_some() {
options.custom_opt_out_directives = self.custom_opt_out_directives;
}
if let Some(gating) = self.gating {
options.gating = Some(GatingConfig {
source: gating.source,
import_specifier_name: gating.import_specifier_name,
});
}
if let Some(dynamic_gating) = self.dynamic_gating {
options.dynamic_gating = Some(DynamicGatingConfig { source: dynamic_gating.source });
}
if let Some(environment) = self.environment {
environment.apply_to(&mut options.environment)?;
}
Ok(options)
}
}
impl ReactCompilerEnvironmentOptions {
fn apply_to(self, environment: &mut EnvironmentConfig) -> Result<(), OxcDiagnostic> {
macro_rules! apply_bool_options {
($($field:ident),+ $(,)?) => {
$(
if let Some(value) = self.$field {
environment.$field = value;
}
)+
};
}
if self.custom_macros.is_some() {
environment.custom_macros = self.custom_macros;
}
if self.enable_reset_cache_on_source_file_changes.is_some() {
environment.enable_reset_cache_on_source_file_changes =
self.enable_reset_cache_on_source_file_changes;
}
if let Some(value) = self.validate_exhaustive_effect_dependencies {
environment.validate_exhaustive_effect_dependencies = match value.as_str() {
"off" => ExhaustiveEffectDepsMode::Off,
"all" => ExhaustiveEffectDepsMode::All,
"missing-only" => ExhaustiveEffectDepsMode::MissingOnly,
"extra-only" => ExhaustiveEffectDepsMode::ExtraOnly,
_ => {
return Err(invalid_option(
"environment.validateExhaustiveEffectDependencies",
&value,
));
}
};
}
if self.validate_no_capitalized_calls.is_some() {
environment.validate_no_capitalized_calls = self.validate_no_capitalized_calls;
}
if self.validate_blocklisted_imports.is_some() {
environment.validate_blocklisted_imports = self.validate_blocklisted_imports;
}
apply_bool_options!(
enable_preserve_existing_memoization_guarantees,
validate_preserve_existing_memoization_guarantees,
validate_exhaustive_memoization_dependencies,
enable_optional_dependencies,
enable_name_anonymous_functions,
validate_hooks_usage,
validate_ref_access_during_render,
validate_no_set_state_in_render,
enable_use_keyed_state,
validate_no_set_state_in_effects,
validate_no_derived_computations_in_effects,
validate_no_derived_computations_in_effects_exp,
validate_no_jsx_in_try_statements,
validate_static_components,
validate_source_locations,
validate_no_impure_functions_in_render,
validate_no_freezing_known_mutable_functions,
enable_assume_hooks_follow_rules_of_react,
enable_transitively_freeze_function_expressions,
enable_function_outlining,
enable_jsx_outlining,
assert_valid_mutable_ranges,
enable_custom_type_definition_for_reanimated,
enable_treat_ref_like_identifiers_as_refs,
enable_treat_set_identifiers_as_state_setters,
validate_no_void_use_memo,
enable_allow_set_state_from_refs_in_effects,
enable_verbose_no_set_state_in_effect,
enable_forest,
);
Ok(())
}
}
fn parse<T: FromStr>(value: &str, option: &str) -> Result<T, OxcDiagnostic> {
T::from_str(value).map_err(|_| invalid_option(option, value))
}
fn invalid_option(option: &str, value: &str) -> OxcDiagnostic {
OxcDiagnostic::error(format!("Invalid React Compiler `{option}` option: `{value}`."))
}
+141
View File
@@ -0,0 +1,141 @@
import { describe, expect, it } from "vitest";
import { transform, transformSync } from "../index";
const fixture = `// @license MIT
import { useState } from "react";
interface Props {
text: string;
}
export function Component(props: Props) {
const [count, setCount] = useState<number>(0);
return <button onClick={() => setCount(count + 1)}>{props.text}: {count}</button>;
}
`;
describe("transformSync", () => {
it("runs React Compiler before TypeScript and JSX transforms", () => {
const result = transformSync("Component.tsx", fixture);
expect(result.errors).toEqual([]);
expect(result.code).toContain("react/compiler-runtime");
expect(result.code).toContain("_c(");
expect(result.code).not.toContain("interface Props");
expect(result.code).not.toContain("<button");
expect(result.code).toContain("@license MIT");
});
it("forwards React Compiler options", () => {
const target = transformSync("Component.tsx", fixture, { 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",
},
});
expect(gated.errors).toEqual([]);
expect(gated.code).toContain("feature-flags");
expect(gated.code).toContain("isCompilerEnabled");
});
it("supports dynamic gating and Meta-internal targets", () => {
const dynamic = transformSync(
"Component.jsx",
`export function Component(props) {
"use memo if(isCompilerEnabled)";
return <div>{props.text}</div>;
}`,
{
dynamicGating: { source: "dynamic-feature-flags" },
},
);
expect(dynamic.errors).toEqual([]);
expect(dynamic.code).toContain("dynamic-feature-flags");
expect(dynamic.code).toContain("isCompilerEnabled");
const meta = transformSync("Component.tsx", fixture, {
target: {
kind: "donotuse_meta_internal",
runtimeModule: "custom-react-runtime",
},
});
expect(meta.errors).toEqual([]);
expect(meta.code).toContain("custom-react-runtime");
});
it("can override opt-out directives", () => {
const source = `function Component(props) {
"use no memo";
return <div>{props.text}</div>;
}`;
const optedOut = transformSync("Component.jsx", source);
expect(optedOut.errors).toEqual([]);
expect(optedOut.code).not.toContain("_c(");
const compiled = transformSync("Component.jsx", source, {
ignoreUseNoForget: true,
});
expect(compiled.errors).toEqual([]);
expect(compiled.code).toContain("_c(");
});
it.each([
["compilationMode", { compilationMode: "bogus" }],
["panicThreshold", { panicThreshold: "bogus" }],
["outputMode", { outputMode: "bogus" }],
["target", { target: "20" }],
[
"environment.validateExhaustiveEffectDependencies",
{
environment: {
validateExhaustiveEffectDependencies: "bogus",
},
},
],
])("reports an invalid %s option without emitting code", (option, options) => {
const result = transformSync("Component.tsx", fixture, options as never);
expect(result.code).toBe("");
expect(result.errors).toHaveLength(1);
expect(result.errors[0].message).toContain(`Invalid React Compiler \`${option}\` option`);
});
it("supports source maps and language overrides", () => {
const result = transformSync("Component.vue", fixture, {
lang: "tsx",
sourcemap: true,
});
expect(result.errors).toEqual([]);
expect(result.map).toMatchObject({
sources: ["Component.vue"],
sourcesContent: [fixture],
version: 3,
});
});
it("can filter files with sources", () => {
const result = transformSync("vendor/Component.tsx", fixture, {
sources: ["src/"],
});
expect(result.errors).toEqual([]);
expect(result.code).not.toContain("react/compiler-runtime");
expect(result.code).not.toContain("interface Props");
expect(result.code).not.toContain("<button");
});
});
describe("transform", () => {
it("matches the synchronous transform", async () => {
const sync = transformSync("Component.tsx", fixture);
const asyncResult = await transform("Component.tsx", fixture);
expect(asyncResult).toEqual(sync);
});
});
@@ -0,0 +1,62 @@
import {
createOnMessage as __wasmCreateOnMessageForFsProxy,
getDefaultContext as __emnapiGetDefaultContext,
instantiateNapiModuleSync as __emnapiInstantiateNapiModuleSync,
WASI as __WASI,
} from '@napi-rs/wasm-runtime'
const __wasi = new __WASI({
version: 'preview1',
})
const __wasmUrl = new URL('./transform-react.wasm32-wasi.wasm', import.meta.url).href
const __emnapiContext = __emnapiGetDefaultContext()
const __sharedMemory = new WebAssembly.Memory({
initial: 4000,
maximum: 65536,
shared: true,
})
const __wasmFile = await fetch(__wasmUrl).then((res) => res.arrayBuffer())
const {
instance: __napiInstance,
module: __wasiModule,
napiModule: __napiModule,
} = __emnapiInstantiateNapiModuleSync(__wasmFile, {
context: __emnapiContext,
asyncWorkPoolSize: 4,
wasi: __wasi,
onCreateWorker() {
const worker = new Worker(new URL('./wasi-worker-browser.mjs', import.meta.url), {
type: 'module',
})
return worker
},
overwriteImports(importObject) {
importObject.env = {
...importObject.env,
...importObject.napi,
...importObject.emnapi,
memory: __sharedMemory,
}
return importObject
},
beforeInit({ instance }) {
for (const name of Object.keys(instance.exports)) {
if (name.startsWith('__napi_register__')) {
instance.exports[name]()
}
}
},
})
export default __napiModule.exports
export const Severity = __napiModule.exports.Severity
export const transform = __napiModule.exports.transform
export const transformSync = __napiModule.exports.transformSync
@@ -0,0 +1,113 @@
/* eslint-disable */
/* prettier-ignore */
/* auto-generated by NAPI-RS */
const __nodeFs = require('node:fs')
const __nodePath = require('node:path')
const { WASI: __nodeWASI } = require('node:wasi')
const { Worker } = require('node:worker_threads')
const {
createOnMessage: __wasmCreateOnMessageForFsProxy,
getDefaultContext: __emnapiGetDefaultContext,
instantiateNapiModuleSync: __emnapiInstantiateNapiModuleSync,
} = require('@napi-rs/wasm-runtime')
const __rootDir = __nodePath.parse(process.cwd()).root
const __wasi = new __nodeWASI({
version: 'preview1',
env: process.env,
preopens: {
[__rootDir]: __rootDir,
}
})
const __emnapiContext = __emnapiGetDefaultContext()
const __sharedMemory = new WebAssembly.Memory({
initial: 4000,
maximum: 65536,
shared: true,
})
let __wasmFilePath = __nodePath.join(__dirname, 'transform-react.wasm32-wasi.wasm')
const __wasmDebugFilePath = __nodePath.join(__dirname, 'transform-react.wasm32-wasi.debug.wasm')
if (__nodeFs.existsSync(__wasmDebugFilePath)) {
__wasmFilePath = __wasmDebugFilePath
} else if (!__nodeFs.existsSync(__wasmFilePath)) {
try {
__wasmFilePath = require.resolve('@oxc-transform-react/binding-wasm32-wasi/transform-react.wasm32-wasi.wasm')
} catch {
throw new Error('Cannot find transform-react.wasm32-wasi.wasm file, and @oxc-transform-react/binding-wasm32-wasi package is not installed.')
}
}
const { instance: __napiInstance, module: __wasiModule, napiModule: __napiModule } = __emnapiInstantiateNapiModuleSync(__nodeFs.readFileSync(__wasmFilePath), {
context: __emnapiContext,
asyncWorkPoolSize: (function() {
const threadsSizeFromEnv = Number(process.env.NAPI_RS_ASYNC_WORK_POOL_SIZE ?? process.env.UV_THREADPOOL_SIZE)
// NaN > 0 is false
if (threadsSizeFromEnv > 0) {
return threadsSizeFromEnv
} else {
return 4
}
})(),
reuseWorker: true,
wasi: __wasi,
onCreateWorker() {
const worker = new Worker(__nodePath.join(__dirname, 'wasi-worker.mjs'), {
env: process.env,
})
worker.onmessage = ({ data }) => {
__wasmCreateOnMessageForFsProxy(__nodeFs)(data)
}
// The main thread of Node.js waits for all the active handles before exiting.
// But Rust threads are never waited without `thread::join`.
// So here we hack the code of Node.js to prevent the workers from being referenced (active).
// According to https://github.com/nodejs/node/blob/19e0d472728c79d418b74bddff588bea70a403d0/lib/internal/worker.js#L415,
// a worker is consist of two handles: kPublicPort and kHandle.
{
const kPublicPort = Object.getOwnPropertySymbols(worker).find(s =>
s.toString().includes("kPublicPort")
);
if (kPublicPort) {
worker[kPublicPort].ref = () => {};
}
const kHandle = Object.getOwnPropertySymbols(worker).find(s =>
s.toString().includes("kHandle")
);
if (kHandle) {
worker[kHandle].ref = () => {};
}
worker.unref();
}
return worker
},
overwriteImports(importObject) {
importObject.env = {
...importObject.env,
...importObject.napi,
...importObject.emnapi,
memory: __sharedMemory,
}
return importObject
},
beforeInit({ instance }) {
for (const name of Object.keys(instance.exports)) {
if (name.startsWith('__napi_register__')) {
instance.exports[name]()
}
}
},
})
module.exports = __napiModule.exports
module.exports.Severity = __napiModule.exports.Severity
module.exports.transform = __napiModule.exports.transform
module.exports.transformSync = __napiModule.exports.transformSync
+4
View File
@@ -0,0 +1,4 @@
{
"files": [],
"references": [{ "path": "./tsconfig.node.json" }]
}
+11
View File
@@ -0,0 +1,11 @@
{
"compilerOptions": {
"lib": ["ESNext"],
"module": "Preserve",
"moduleResolution": "Bundler",
"noEmit": true,
"target": "ESNext",
"types": ["node"],
"skipLibCheck": true
}
}
@@ -0,0 +1,34 @@
import { instantiateNapiModuleSync, MessageHandler, WASI } from '@napi-rs/wasm-runtime'
const handler = new MessageHandler({
onLoad({ wasmModule, wasmMemory }) {
const wasi = new WASI({
print: function () {
// eslint-disable-next-line no-console
console.log.apply(console, arguments)
},
printErr: function() {
// eslint-disable-next-line no-console
console.error.apply(console, arguments)
},
})
return instantiateNapiModuleSync(wasmModule, {
childThread: true,
wasi,
overwriteImports(importObject) {
importObject.env = {
...importObject.env,
...importObject.napi,
...importObject.emnapi,
memory: wasmMemory,
}
},
})
},
})
globalThis.onmessage = function (e) {
handler.handle(e)
}
+63
View File
@@ -0,0 +1,63 @@
import fs from "node:fs";
import { createRequire } from "node:module";
import { parse } from "node:path";
import { WASI } from "node:wasi";
import { parentPort, Worker } from "node:worker_threads";
const require = createRequire(import.meta.url);
const { instantiateNapiModuleSync, MessageHandler, getDefaultContext } = require("@napi-rs/wasm-runtime");
if (parentPort) {
parentPort.on("message", (data) => {
globalThis.onmessage({ data });
});
}
Object.assign(globalThis, {
self: globalThis,
require,
Worker,
importScripts: function (f) {
;(0, eval)(fs.readFileSync(f, "utf8") + "//# sourceURL=" + f);
},
postMessage: function (msg) {
if (parentPort) {
parentPort.postMessage(msg);
}
},
});
const emnapiContext = getDefaultContext();
const __rootDir = parse(process.cwd()).root;
const handler = new MessageHandler({
onLoad({ wasmModule, wasmMemory }) {
const wasi = new WASI({
version: 'preview1',
env: process.env,
preopens: {
[__rootDir]: __rootDir,
},
});
return instantiateNapiModuleSync(wasmModule, {
childThread: true,
wasi,
context: emnapiContext,
overwriteImports(importObject) {
importObject.env = {
...importObject.env,
...importObject.napi,
...importObject.emnapi,
memory: wasmMemory
};
},
});
},
});
globalThis.onmessage = function (e) {
handler.handle(e);
};
@@ -0,0 +1,23 @@
const fs = require("node:fs");
const childProcess = require("node:child_process");
const pkg = JSON.parse(
fs.readFileSync(require.resolve("oxc-transform-react/package.json"), "utf-8"),
);
const { version } = pkg;
const baseDir = `/tmp/oxc-transform-react-${version}`;
const bindingEntry = `${baseDir}/node_modules/@oxc-transform-react/binding-wasm32-wasi/transform-react.wasi.cjs`;
if (!fs.existsSync(bindingEntry)) {
fs.rmSync(baseDir, { recursive: true, force: true });
fs.mkdirSync(baseDir, { recursive: true });
const bindingPkg = `@oxc-transform-react/binding-wasm32-wasi@${version}`;
// oxlint-disable-next-line no-console
console.log(`[oxc-transform-react] Downloading ${bindingPkg} on WebContainer...`);
childProcess.execFileSync("pnpm", ["i", bindingPkg], {
cwd: baseDir,
stdio: "inherit",
});
}
module.exports = require(bindingEntry);
+8 -8
View File
@@ -29,14 +29,14 @@
"apps/oxlint/conformance/submodules/**",
"napi/parser/src-js/bindings.js",
"napi/playground/playground.wasi-browser.js",
"napi/{transform,minify,playground}/index.js",
"napi/{transform,minify,playground}/index.d.ts",
"napi/{parser,transform,minify,playground}/**/index.d.ts",
"napi/{parser,transform,minify,playground}/**/*.wasi-browser.js",
"napi/{parser,transform,minify,playground}/**/*.wasi.cjs",
"napi/{parser,transform,minify,playground}/**/wasi-worker-browser.mjs",
"napi/{parser,transform,minify,playground}/**/wasi-worker.mjs",
"napi/{parser,transform,minify,playground}/**/browser.js",
"napi/{transform,transform-react,minify,playground}/index.js",
"napi/{transform,transform-react,minify,playground}/index.d.ts",
"napi/{parser,transform,transform-react,minify,playground}/**/index.d.ts",
"napi/{parser,transform,transform-react,minify,playground}/**/*.wasi-browser.js",
"napi/{parser,transform,transform-react,minify,playground}/**/*.wasi.cjs",
"napi/{parser,transform,transform-react,minify,playground}/**/wasi-worker-browser.mjs",
"napi/{parser,transform,transform-react,minify,playground}/**/wasi-worker.mjs",
"napi/{parser,transform,transform-react,minify,playground}/**/browser.js",
"npm/runtime/src",
"npm/oxc-types/types.d.ts",
"npm/oxfmt/configuration_schema.json",
+21
View File
@@ -352,6 +352,27 @@ importers:
specifier: 'catalog:'
version: 4.1.10(@types/node@24.1.0)(@vitest/browser-playwright@4.1.10)(@vitest/browser-preview@4.1.10)(happy-dom@20.0.11)(vite@7.3.0(@types/node@24.1.0)(lightningcss@1.33.0)(terser@5.44.1)(tsx@4.23.1))
napi/transform-react:
devDependencies:
'@emnapi/core':
specifier: 'catalog:'
version: 1.11.2
'@emnapi/runtime':
specifier: 'catalog:'
version: 1.11.2
'@napi-rs/cli':
specifier: 'catalog:'
version: 3.7.4(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.1.0)
'@types/node':
specifier: 'catalog:'
version: 24.1.0
publint:
specifier: 'catalog:'
version: 0.3.22
vitest:
specifier: 'catalog:'
version: 4.1.10(@types/node@24.1.0)(@vitest/browser-playwright@4.1.10)(@vitest/browser-preview@4.1.10)(happy-dom@20.0.11)(vite@7.3.0(@types/node@24.1.0)(lightningcss@1.33.0)(terser@5.44.1)(tsx@4.23.1))
npm/oxc-types: {}
npm/oxfmt: