feat(react-compiler): scaffold SWC port of Babel entrypoint#11687
Conversation
🦋 Changeset detectedLatest commit: 4190543 The changes in this PR will be included in the next version bump. Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
Warning Review the following alerts detected in dependencies. According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.
|
Binary Sizes
Commit: 1b29e25 |
4544fb4 to
454c859
Compare
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Pull request overview
Scaffolds an initial SWC-based port of the React Compiler “Babel entrypoint” into a new swc_ecma_react_compiler crate, including a public options/error API surface, a program-level compilation entrypoint, and a first-pass fixture harness.
Changes:
- Introduces a new
compile_program/compile_fnentrypoint with suppression handling, directive parsing, import injection, and a staged (currently mostly no-op) pipeline skeleton. - Adds public configuration types (options, targets, logger events) and structured error/diagnostic types.
- Adds fixture-based tests plus an upstream fixture sync script and manifest.
Reviewed changes
Copilot reviewed 26 out of 27 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| crates/swc_ecma_react_compiler/src/lib.rs | Exposes public API + SWC pass wrapper around compile_program. |
| crates/swc_ecma_react_compiler/src/options.rs | Defines plugin options, defaults, parsing, and target/runtime selection. |
| crates/swc_ecma_react_compiler/src/error.rs | Introduces compiler error + diagnostic detail structures. |
| crates/swc_ecma_react_compiler/src/entrypoint/program.rs | Implements the program-level compiler traversal, selection logic, and reporting. |
| crates/swc_ecma_react_compiler/src/entrypoint/imports.rs | Adds runtime/external import detection + insertion helpers. |
| crates/swc_ecma_react_compiler/src/entrypoint/gating.rs | Parses use memo if(...) dynamic gating directives. |
| crates/swc_ecma_react_compiler/src/entrypoint/suppression.rs | Parses ESLint/Flow suppression comments into skip diagnostics. |
| crates/swc_ecma_react_compiler/src/utils/mod.rs | Adds directive collection + naming/identifier helpers. |
| crates/swc_ecma_react_compiler/src/{hir,inference,ssa,optimization,validation,transform,reactive_scopes}/mod.rs | Adds pipeline stage modules as placeholders for subsequent porting work. |
| crates/swc_ecma_react_compiler/tests/fixture.rs | Fixture harness executing compile_program and asserting output/errors. |
| crates/swc_ecma_react_compiler/tests/fixtures/** | Adds initial local fixtures + upstream fixture manifest seed. |
| crates/swc_ecma_react_compiler/scripts/sync_fixtures.sh | Script to sync upstream fixtures via gh api. |
| crates/swc_ecma_react_compiler/Cargo.toml | Adds required dependencies/dev-dependencies for tests/harness. |
| Cargo.lock | Lockfile updates for new deps. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| module.body.insert( | ||
| 0, | ||
| ModuleItem::ModuleDecl(ModuleDecl::Import(ImportDecl { | ||
| span: DUMMY_SP, | ||
| specifiers: vec![new_specifier], | ||
| src: Box::new(Str { | ||
| span: DUMMY_SP, | ||
| value: Atom::new(module_name).into(), | ||
| raw: None, | ||
| }), | ||
| type_only: false, | ||
| with: None, | ||
| phase: ImportPhase::Evaluation, | ||
| })), | ||
| ); |
| let local = Ident::new_no_ctxt(Atom::new(local_hint), DUMMY_SP); | ||
|
|
||
| let new_specifier = ImportSpecifier::Named(ImportNamedSpecifier { | ||
| span: DUMMY_SP, | ||
| local: local.clone(), | ||
| imported: Some(ModuleExportName::Ident(Ident::new_no_ctxt( | ||
| Atom::new(imported_name), | ||
| DUMMY_SP, | ||
| ))), | ||
| is_type_only: false, | ||
| }); | ||
|
|
||
| if let Some(import_decl) = find_existing_import_mut(module, module_name) { | ||
| import_decl.specifiers.push(new_specifier); | ||
| return Some(local); |
| let block = match &*arrow.body { | ||
| BlockStmtOrExpr::BlockStmt(block) => block, | ||
| BlockStmtOrExpr::Expr(_) => { | ||
| // We compile concise-body arrows by wrapping into a block. | ||
| let expr = match *arrow.body.clone() { | ||
| BlockStmtOrExpr::Expr(expr) => expr, | ||
| BlockStmtOrExpr::BlockStmt(_) => unreachable!(), | ||
| }; | ||
| arrow.body = Box::new(BlockStmtOrExpr::BlockStmt(BlockStmt { | ||
| span: arrow.span, | ||
| ctxt: arrow.ctxt, | ||
| stmts: vec![swc_ecma_ast::Stmt::Return(swc_ecma_ast::ReturnStmt { | ||
| span: arrow.span, | ||
| arg: Some(expr), | ||
| })], | ||
| })); | ||
|
|
||
| match &*arrow.body { | ||
| BlockStmtOrExpr::BlockStmt(block) => block, | ||
| BlockStmtOrExpr::Expr(_) => unreachable!(), | ||
| } |
|
|
||
| program.visit_mut_children_with(self); |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 454c859574
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
Addressed review #11687 (review) in 710d12c:
|
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 710d12cd3c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| BlockStmtOrExpr::Expr(expr) => expr, | ||
| BlockStmtOrExpr::BlockStmt(_) => unreachable!(), | ||
| }; | ||
| arrow.body = Box::new(BlockStmtOrExpr::BlockStmt(BlockStmt { |
There was a problem hiding this comment.
Defer concise-arrow rewriting until compilation is confirmed
compile_arrow rewrites every concise arrow body into a block (arrow.body = ...) before deciding whether the function should actually be compiled. When selected_type is None (for example, non-React names in infer mode, annotation mode without opt-in, or lint/no-emit flows), the function returns early and leaves this rewrite behind, so code gets mutated even though the compiler reported no transformation for that function.
Useful? React with 👍 / 👎.
| open_eslint_disable = Some(comment.clone()); | ||
| } | ||
|
|
||
| let is_enable = text.contains("eslint-enable") && has_rule; |
There was a problem hiding this comment.
Accept bare eslint-enable as ending a suppression range
Suppression parsing only treats eslint-enable as a close marker when the comment text also contains one of the configured rule names (&& has_rule). A common pattern like /* eslint-disable react-hooks/rules-of-hooks */ ... /* eslint-enable */ will never close, so the disable is treated as active to EOF and unrelated later functions are incorrectly skipped as suppressed.
Useful? React with 👍 / 👎.
| return Some(existing_local); | ||
| } | ||
|
|
||
| let local = Ident::new_no_ctxt(Atom::new(local_hint), DUMMY_SP); |
There was a problem hiding this comment.
Avoid fixed local alias when injecting runtime imports
The helper import path always creates a local identifier from local_hint (for memo cache this is _c) without checking existing top-level bindings. If the module already defines _c, emitting import { c as _c } ... introduces a duplicate binding and invalid output, so the inserted import alias needs collision handling before insertion.
Useful? React with 👍 / 👎.
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub. |
PR Review: feat(react-compiler): scaffold SWC port of Babel entrypointOverall this is a well-structured scaffold that faithfully mirrors the Babel plugin's entrypoint logic. The architecture is clean: options parsing, entrypoint orchestration, and pipeline stage stubs are well-separated. Here are findings organized by category: Code Quality
Potential Bugs
Performance
Security
Test Coverage
Minor Nits
Overall: solid scaffolding with good separation of concerns. The main actionable items are the potential double-visitation bug (#5), the large duplication between named/arrow compilation (#1), and expanding test coverage for the various compilation mode branches (#13). |
There was a problem hiding this comment.
Pull request overview
Scaffolds a new swc_ecma_react_compiler crate entrypoint and option/error surface, plus an initial (mostly no-op) staged pipeline and fixture harness to validate directive/gating/suppression behavior while the Rust port is filled in.
Changes:
- Adds public API types for options, errors/diagnostics, and a SWC
Passwrapper +compile_program/compile_fnentrypoints. - Implements program-level traversal for function selection, directive opt-in/opt-out, suppression handling, import injection, and outlined-function queue insertion.
- Adds pipeline stage modules (HIR/SSA/inference/optimization/validation/transform/utils) as placeholders and introduces fixture + upstream fixture sync script.
Reviewed changes
Copilot reviewed 27 out of 28 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| crates/swc_ecma_react_compiler/tests/fixtures/upstream_manifest.txt | Manifest of upstream fixture names for sync script. |
| crates/swc_ecma_react_compiler/tests/fixtures/module-opt-out/input.js | Fixture input for module opt-out directive. |
| crates/swc_ecma_react_compiler/tests/fixtures/module-opt-out/output.js | Expected output for module opt-out fixture. |
| crates/swc_ecma_react_compiler/tests/fixtures/dynamic-gating-invalid/input.js | Fixture input for invalid dynamic gating directive. |
| crates/swc_ecma_react_compiler/tests/fixtures/dynamic-gating-invalid/error.txt | Expected error text for invalid gating fixture. |
| crates/swc_ecma_react_compiler/tests/fixtures/basic-component/input.js | Basic component fixture input. |
| crates/swc_ecma_react_compiler/tests/fixtures/basic-component/output.js | Expected output including runtime import insertion. |
| crates/swc_ecma_react_compiler/tests/fixture.rs | Fixture runner: parse/compile/print and compare output or error. |
| crates/swc_ecma_react_compiler/src/validation/mod.rs | Placeholder validation stage functions (no-op). |
| crates/swc_ecma_react_compiler/src/utils/mod.rs | Helpers for directive collection and name classification/identifier validation. |
| crates/swc_ecma_react_compiler/src/transform/mod.rs | React function kind enum + placeholder transform hook. |
| crates/swc_ecma_react_compiler/src/ssa/mod.rs | Placeholder SSA stage functions (no-op). |
| crates/swc_ecma_react_compiler/src/reactive_scopes/mod.rs | Placeholder reactive scopes IR + codegen payload structs. |
| crates/swc_ecma_react_compiler/src/options.rs | Public options/config types and parsing/defaulting logic + tests. |
| crates/swc_ecma_react_compiler/src/optimization/mod.rs | Placeholder optimization stage functions (no-op). |
| crates/swc_ecma_react_compiler/src/lib.rs | Crate module wiring, re-exports, and SWC Pass wrapper. |
| crates/swc_ecma_react_compiler/src/inference/mod.rs | Placeholder inference stage functions (no-op). |
| crates/swc_ecma_react_compiler/src/hir/mod.rs | Placeholder HIR lowering struct + stub lower. |
| crates/swc_ecma_react_compiler/src/error.rs | Error/diagnostic data model and formatting helpers. |
| crates/swc_ecma_react_compiler/src/entrypoint/suppression.rs | ESLint/Flow suppression detection + conversion to compiler errors + tests. |
| crates/swc_ecma_react_compiler/src/entrypoint/program.rs | Main program entrypoint: traversal, selection, gating, import insertion, outline queue. |
| crates/swc_ecma_react_compiler/src/entrypoint/mod.rs | Entrypoint module wiring + public re-exports. |
| crates/swc_ecma_react_compiler/src/entrypoint/imports.rs | Runtime/external import detection and insertion + tests. |
| crates/swc_ecma_react_compiler/src/entrypoint/gating.rs | Dynamic gating directive parsing/validation + tests. |
| crates/swc_ecma_react_compiler/scripts/sync_fixtures.sh | Script to fetch upstream fixtures via GitHub API and materialize local inputs/outputs. |
| crates/swc_ecma_react_compiler/Cargo.toml | Adds needed dependencies/dev-dependencies for atoms and codegen/parser in tests. |
| Cargo.lock | Locks new crate dependencies. |
| .changeset/green-owls-jump.md | Declares a major changeset for the new scaffolded feature. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| let block = match &*arrow.body { | ||
| BlockStmtOrExpr::BlockStmt(block) => block, | ||
| BlockStmtOrExpr::Expr(_) => { | ||
| // We compile concise-body arrows by wrapping into a block. | ||
| let expr = match *arrow.body.clone() { | ||
| BlockStmtOrExpr::Expr(expr) => expr, | ||
| BlockStmtOrExpr::BlockStmt(_) => unreachable!(), | ||
| }; | ||
| arrow.body = Box::new(BlockStmtOrExpr::BlockStmt(BlockStmt { | ||
| span: arrow.span, | ||
| ctxt: arrow.ctxt, | ||
| stmts: vec![swc_ecma_ast::Stmt::Return(swc_ecma_ast::ReturnStmt { | ||
| span: arrow.span, | ||
| arg: Some(expr), | ||
| })], | ||
| })); | ||
|
|
||
| match &*arrow.body { | ||
| BlockStmtOrExpr::BlockStmt(block) => block, | ||
| BlockStmtOrExpr::Expr(_) => unreachable!(), | ||
| } |
| self.function_depth += 1; | ||
| fn_expr.function.visit_mut_children_with(self); | ||
| self.function_depth -= 1; | ||
| return; | ||
| } | ||
| Expr::Arrow(arrow) => { | ||
| self.compile_arrow(name.as_ref(), arrow, is_top_level, arrow.span); | ||
|
|
||
| self.function_depth += 1; | ||
| arrow.body.visit_mut_with(self); | ||
| self.function_depth -= 1; | ||
| return; |
| if let Some(existing_local) = find_existing_named_import(module, module_name, imported_name) { | ||
| return Some(existing_local); | ||
| } | ||
|
|
||
| let local = Ident::new_no_ctxt(Atom::new(local_hint), DUMMY_SP); | ||
|
|
Summary
Tests
Notes