Commit Graph

8 Commits

Author SHA1 Message Date
Boshen 5d8ccf510b fix(react_compiler): preserve nonfatal diagnostics (#25066)
Preserves nonfatal React Compiler diagnostics and covers warning bailouts through the N-API binding.

AI-assisted.
2026-07-29 13:09:20 +00:00
Boshen 6427542c8d refactor(react_compiler): split public API into compile + in-place CompileOutput::transform (#24401)
Splits `oxc_react_compiler`'s public API into two phases so the transform rewrites the caller's program in place instead of returning a whole-program clone:

- `compile(&Program, &Semantic, &Allocator, PluginOptions) -> (Option<CompileOutput>, Diagnostics)` — read-only; `None` means no changes (or error / lint output mode).
- `CompileOutput::transform(self, &mut Program, &Allocator)` — substitutes the compiled functions into the program in place. `CompileOutput` owns only arena data (replacements + `ProgramContext`), so the caller drops its `Semantic` between the two calls, releasing the shared borrow that previously forced `ox_splice_program` to `clone_in_with_semantic_ids` the entire program.
- `lint` is unchanged and structurally never runs the transform phase; the oxlint rule needed no changes.

The old `transform` free function and `TransformResult` are removed; `oxc_transformer` no longer does the `*program = result.program.take()` swap and just applies the output to its `&mut Program`.

The 1806-fixture snapshot corpus is byte-identical, and the transform path drops one whole-program clone per compiled file.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-07-12 14:35:19 +00:00
Boshen 1fff15c499 refactor(react_compiler): borrow semantic in compile pipeline (#24063)
Borrow existing semantic data and caller allocator in React Compiler transform/lint paths to avoid hidden rebuild/allocation work.

Transform now returns the compiled program so callers can install it after the semantic borrow ends.

AI-assisted.
2026-07-02 10:17:42 +00:00
Boshen 579f025c7c refactor(react_compiler): trim PluginOptions and remove timing instrumentation (#24042)
Slims down `oxc_react_compiler`'s `PluginOptions` and removes the now-dead timing infrastructure. Four related cleanups:

### Remove `source_code` from `PluginOptions`
It was redundant — `compile` in `lib.rs` always overwrote it with `program.source_text`, and no caller set it. The one consumer (`ProgramContext::new` in `program.rs`) now reads `program.source_text` directly (the oxc `Program` is already in scope there), and the threading block in `lib.rs` is gone.

### `default_plugin_options()` → `impl Default for PluginOptions`
Converted the free function into a manual `Default` impl (the defaults are non-trivial: `should_compile: true`, `target: "19"`, `flow_suppressions: true`, …). All callers now use `PluginOptions::default()`.

### Remove `profiling`
The field was always `false` in practice (no caller set it). Its sole consumer now passes `TimingData::new(false)` — which leads into…

### Remove the timing instrumentation entirely
With `profiling` gone, timing was pure, always-disabled instrumentation: every `context.timing.start/stop` was a no-op and the collected `Vec<TimingEntry>` was always empty and never read (`lib.rs` destructures `CompileResult` with `..`). Removed:
- `timing.rs` (the `TimingData`/`TimingEntry` module) and its `mod` declaration
- ~218 `context.timing.start/stop(...)` calls across `pipeline.rs`
- the `timing` field on `ProgramContext` and on both `CompileResult` variants, plus all initializers

### Verification
- `oxc_react_compiler`: builds, 15/15 tests pass.
- `oxc_linter`: `react_compiler` rule snapshot test passes — **snapshot unchanged** (diagnostics identical).
- `oxc_transformer` react_compiler integration: 2/2 pass; `oxc_benchmark` bench compiles.
- rustc warnings, clippy (`dev-no-debug-assertions`), and `cargo shear` all clean.
2026-07-02 06:21:54 +00:00
Boshen 81101e7749 refactor(react_compiler): transform takes &mut Program, lint takes &Program (#23995)
## Summary

`transform` now rewrites the program **in place** instead of returning a
new one, and `lint` stays **read-only** — aligning with how the rest of
the oxc transform pipeline mutates the AST.

- `transform(program: &mut Program, allocator, options)` writes the
memoized AST back into `program`. `TransformResult` drops its `program:
Option<Program>` field (and lifetime) and gains `changed: bool`.
- `lint(program: &Program, options)` is unchanged — borrowed, read-only.
- Both share a private `compile(&Program) -> (Option<Program>,
Diagnostics, Vec<LoggerEvent>)` pipeline. Keeping the semantic borrow
contained in that call is what lets `transform` do `*program = compiled`
afterwards — `SemanticBuilder::build` takes `&'a Program<'a>`, so an
inlined build would pin the borrow for the whole arena and block the
write.
- `transform_source` now returns `(Program, TransformResult)`.

## Callers updated

- `oxc_transformer::run_react_compiler` — the `*program = compiled` it
used to do now lives inside `transform`; it just checks `result.changed`
before rebuilding scoping.
- `examples/react_compiler.rs`,
`tasks/benchmark/benches/react_compiler.rs` — pass `&mut program`, gate
output on `result.changed`.
- lib.rs smoke tests — `transform_source(...).program.expect(...)` →
destructure `(program, result)` + `assert!(result.changed, ...)`.

## Verification

- `cargo test -p oxc_react_compiler` (38) and `oxc_transformer
--features react_compiler` (9) pass
- `cargo ck` (workspace, all-features/all-targets, `--locked`), clippy
(`dev-no-debug-assertions`), `cargo fmt --check` — clean
- `oxc_linter` (uses `lint`), benchmark target, and example all build

---
🤖 Generated with [Claude Code](https://claude.com/claude-code) —
AI-assisted, reviewed before submission (per the repo AI usage policy).
2026-07-01 13:44:21 +08:00
Boshen 48c6f28c4a refactor(react_compiler): split diagnostics into errors/warnings and simplify transform API (#23168)
## What

Reworks the `oxc_react_compiler` public API:

- Renames the standalone `run` entrypoint to `transform`. It now returns a `TransformResult` instead of taking a `&mut errors` vec and rebuilding `Scoping` internally. Callers use `result.program` (`Some`/`None`) to decide whether to rebuild scoping — the `oxc::Compiler` callsite does exactly this.
- Splits `TransformResult`/`LintResult` diagnostics into separate `errors` and `warnings` (partitioned by `Severity`), and documents `errors` / `warnings` / `events`.
- Adds `#[derive(Default)]` to `TransformResult` for the bail-out paths.
- Inlines the former `transform_impl`; semantic is now built internally, after the cheap prefilter bail-outs (so non-React files skip semantic construction).
- Bumps the `forked_react_compiler` family `0.1.3` → `0.1.4`.

Example and benchmark updated to the new `transform(&program, &allocator, options)` signature.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-06-10 00:17:36 +08:00
Boshen 06f3a8ae15 fix(benchmark): build AstNodes store for react_compiler bench (#23132)
## What

The `Bench react_compiler` job on `main` panics:

```
thread 'main' panicked at oxc_index-5.0.0/src/indexing.rs:38:10:
index out of bounds: the len is 0 but the index is 2
```

[Failing
run.](https://github.com/oxc-project/oxc/actions/runs/27154333851/job/80153249638)

## Why

#23005 made the full `AstNodes` store opt-in via
`SemanticBuilder::with_build_nodes(true)` (default now off). The react
compiler reads `semantic.nodes()` in `convert_scope.rs`, so it needs the
store.

That PR updated every call site that feeds `transform` —
`transform_source`, `lint_source`, `run`, and the example — to enable
`with_build_nodes(true)` and `with_enum_eval(true)`, but missed this
benchmark. With nodes off, `semantic.nodes()` is an empty `IndexVec` and
the first `NodeId` access panics. PR CI didn't catch it because the
benchmark only runs on `main`.

## How

Match the other call sites: enable `with_build_nodes(true)` and
`with_enum_eval(true)` in the benchmark's `SemanticBuilder`.

Verified locally by building with `--features react_compiler` and
running the bench across all 5 inputs (RadixUIAdoptionSection.jsx,
react.development.js, App.tsx, binder.ts, kitchen-sink.tsx) — all
complete, no panic.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-06-09 02:13:51 +08:00
Boshen b846ab2d9a feat(react_compiler): integrate the Rust port of the React Compiler (#22942)
Closes https://github.com/oxc-project/oxc/issues/10048

Integrates the Rust port of the React Compiler ([facebook/react#36173](https://github.com/facebook/react/pull/36173)) into oxc.

## Notes (resolved)

- Published crates can't reference Git URLs, so using this from Rolldown needs the React Compiler crates on crates.io. ✅ Published as a fork at https://crates.io/crates/forked_react_compiler; this PR depends on those `forked_react_compiler*` crates so people can get earlier access.
- The React Compiler crates had no license field. ✅ The published fork carries `license = "MIT"` (per the React repo's MIT license), so Cargo Deny and Security Analysis pass.

## Benchmark

**Wall-clock overhead vs plain transform** (release `transformSync`, same `jsx: automatic`, toggling only `reactCompiler`):

| fixture | without | with | overhead |
| --- | --- | --- | --- |
| RadixUIAdoptionSection.jsx (2.5 KiB) | 0.03 ms | 1.81 ms | +1.8 ms |
| excalidraw `App.tsx` (406 KiB) | 4.09 ms | 14.49 ms | +10.4 ms (3.5×) |

So roughly a fixed ~1.8 ms floor per file plus a per-size cost — about **3.5× the transform time** on a large real-world component.

## Binary size

Linking the React Compiler pulls the whole compiler pipeline (HIR, lowering, inference, SSA, optimization, reactive scopes, validation) plus the oxc⇄Babel AST conversion into the binding. Release build of the napi transform addon (`transform.darwin-arm64.node`, `--release`, stripped), darwin-arm64:

| build | size |
| --- | --- |
| baseline (`main`) | 3.51 MiB |
| with React Compiler | 8.66 MiB |
| **delta** | **+5.14 MiB (+146%, 2.46×)** |
2026-06-08 13:32:56 +00:00