Files
react/compiler/CLAUDE.md
Joseph Savona b16b768fbd [compiler] Feature flag cleanup (#35825)
Cleans up feature flags that do not have an active experiment and which
we don't currently plan to ship, one commit per flag. Notable removals:
* Automatic (inferred) effect dependencies / Fire: abandoned due to
early feedback. Shipped useEffectEvent which addresses some of the
use-cases.
* Inline JSX transform (experimented, not a consistent win)
* Context selectors (experimented, not a sufficient/consistent win given
the benefit the compiler already provides)
* Instruction Reordering (will try a different approach)

To decide which features to remove, I looked at Meta's internal repos as
well as eslint-pugin-react-hooks to see which flags were never
overridden anywhere. That gave a longer list of flags, from which I then
removed some features that I know are used in OSS.
2026-02-20 12:29:12 -08:00

8.7 KiB

React Compiler Knowledge Base

This document contains knowledge about the React Compiler gathered during development sessions. It serves as a reference for understanding the codebase architecture and key concepts.

Project Structure

When modifying the compiler, you MUST read the documentation about that pass in compiler/packages/babel-plugin-react-compiler/docs/passes/ to learn more about the role of that pass within the compiler.

  • packages/babel-plugin-react-compiler/ - Main compiler package
    • src/HIR/ - High-level Intermediate Representation types and utilities
    • src/Inference/ - Effect inference passes (aliasing, mutation, etc.)
    • src/Validation/ - Validation passes that check for errors
    • src/Entrypoint/Pipeline.ts - Main compilation pipeline with pass ordering
    • src/__tests__/fixtures/compiler/ - Test fixtures
      • error.todo-*.js - Unsupported feature, correctly throws Todo error (graceful bailout)
      • error.bug-*.js - Known bug, throws wrong error type or incorrect behavior
      • *.expect.md - Expected output for each fixture

Running Tests

# Run all tests
yarn snap

# Run tests matching a pattern
# Example: yarn snap -p 'error.*'
yarn snap -p <pattern>

# Run a single fixture in debug mode. Use the path relative to the __tests__/fixtures/compiler directory
# For each step of compilation, outputs the step name and state of the compiled program
# Example: yarn snap -p simple.js -d
yarn snap -p <file-basename> -d

# Update fixture outputs (also works with -p)
yarn snap -u

Compiling Arbitrary Files

Use yarn snap compile to compile any file (not just fixtures) with the React Compiler:

# Compile a file and see the output
yarn snap compile <path>

# Compile with debug logging to see the state after each compiler pass
# This is an alternative to `yarn snap -d -p <pattern>` when you don't have a fixture file yet
yarn snap compile --debug <path>

Minimizing Test Cases

Use yarn snap minimize to automatically reduce a failing test case to its minimal reproduction:

# Minimize a file that causes a compiler error
yarn snap minimize <path>

# Minimize and update the file in-place with the minimized version
yarn snap minimize --update <path>

Version Control

This repository uses Sapling (sl) for version control. Sapling is similar to Mercurial: there is not staging area, but new/deleted files must be explicitlyu added/removed.

# Check status
sl status

# Add new files, remove deleted files
sl addremove

# Commit all changes
sl commit -m "Your commit message"

# Commit with multi-line message using heredoc
sl commit -m "$(cat <<'EOF'
Summary line

Detailed description here
EOF
)"

Key Concepts

HIR (High-level Intermediate Representation)

The compiler converts source code to HIR for analysis. Key types in src/HIR/HIR.ts:

  • HIRFunction - A function being compiled

    • body.blocks - Map of BasicBlocks
    • context - Captured variables from outer scope
    • params - Function parameters
    • returns - The function's return place
    • aliasingEffects - Effects that describe the function's behavior when called
  • Instruction - A single operation

    • lvalue - The place being assigned to
    • value - The instruction kind (CallExpression, FunctionExpression, LoadLocal, etc.)
    • effects - Array of AliasingEffects for this instruction
  • Terminal - Block terminators (return, branch, etc.)

    • effects - Array of AliasingEffects
  • Place - A reference to a value

    • identifier.id - Unique IdentifierId
  • Phi nodes - Join points for values from different control flow paths

    • Located at block.phis
    • phi.place - The result place
    • phi.operands - Map of predecessor block to source place

AliasingEffects System

Effects describe data flow and operations. Defined in src/Inference/AliasingEffects.ts:

Data Flow Effects:

  • Impure - Marks a place as containing an impure value (e.g., Date.now() result, ref.current)
  • Capture a -> b - Value from a is captured into b (mutable capture)
  • Alias a -> b - b aliases a
  • ImmutableCapture a -> b - Immutable capture (like Capture but read-only)
  • Assign a -> b - Direct assignment
  • MaybeAlias a -> b - Possible aliasing
  • CreateFrom a -> b - Created from source

Mutation Effects:

  • Mutate value - Value is mutated
  • MutateTransitive value - Value and transitive captures are mutated
  • MutateConditionally value - May mutate
  • MutateTransitiveConditionally value - May mutate transitively

Other Effects:

  • Render place - Place is used in render context (JSX props, component return)
  • Freeze place - Place is frozen (made immutable)
  • Create place - New value created
  • CreateFunction - Function expression created, includes captures array
  • Apply - Function application with receiver, function, args, and result

Hook Aliasing Signatures

Located in src/HIR/Globals.ts, hooks can define custom aliasing signatures to control how data flows through them.

Structure:

aliasing: {
  receiver: '@receiver',    // The hook function itself
  params: ['@param0'],      // Named positional parameters
  rest: '@rest',            // Rest parameters (or null)
  returns: '@returns',      // Return value
  temporaries: [],          // Temporary values during execution
  effects: [                // Array of effects to apply when hook is called
    {kind: 'Freeze', value: '@param0', reason: ValueReason.HookCaptured},
    {kind: 'Assign', from: '@param0', into: '@returns'},
  ],
}

Common patterns:

  1. RenderHookAliasing (useState, useContext, useMemo, useCallback):

    • Freezes arguments (Freeze @rest)
    • Marks arguments as render-time (Render @rest)
    • Creates frozen return value
    • Aliases arguments to return
  2. EffectHookAliasing (useEffect, useLayoutEffect, useInsertionEffect):

    • Freezes function and deps
    • Creates internal effect object
    • Captures function and deps into effect
    • Returns undefined
  3. Event handler hooks (useEffectEvent):

    • Freezes callback (Freeze @fn)
    • Aliases input to return (Assign @fn -> @returns)
    • NO Render effect (callback not called during render)

Example: useEffectEvent

const UseEffectEventHook = addHook(
  DEFAULT_SHAPES,
  {
    positionalParams: [Effect.Freeze],  // Takes one positional param
    restParam: null,
    returnType: {kind: 'Function', ...},
    calleeEffect: Effect.Read,
    hookKind: 'useEffectEvent',
    returnValueKind: ValueKind.Frozen,
    aliasing: {
      receiver: '@receiver',
      params: ['@fn'],              // Name for the callback parameter
      rest: null,
      returns: '@returns',
      temporaries: [],
      effects: [
        {kind: 'Freeze', value: '@fn', reason: ValueReason.HookCaptured},
        {kind: 'Assign', from: '@fn', into: '@returns'},
        // Note: NO Render effect - callback is not called during render
      ],
    },
  },
  BuiltInUseEffectEventId,
);

// Add as both names for compatibility
['useEffectEvent', UseEffectEventHook],
['experimental_useEffectEvent', UseEffectEventHook],

Key insight: If a hook is missing an aliasing config, it falls back to DefaultNonmutatingHook which includes a Render effect on all arguments. This can cause false positives for hooks like useEffectEvent whose callbacks are not called during render.

Feature Flags

Feature flags are configured in src/HIR/Environment.ts, for example enableJsxOutlining. Test fixtures can override the active feature flags used for that fixture via a comment pragma on the first line of the fixture input, for example:

// enableJsxOutlining @enableNameAnonymousFunctions:false

...code...

Would enable the enableJsxOutlining feature and disable the enableNameAnonymousFunctions feature.

Debugging Tips

  1. Run yarn snap -p <fixture> to see full HIR output with effects
  2. Look for @aliasingEffects= on FunctionExpressions
  3. Look for Impure, Render, Capture effects on instructions
  4. Check the pass ordering in Pipeline.ts to understand when effects are populated vs validated

Error Handling for Unsupported Features

When the compiler encounters an unsupported but known pattern, use CompilerError.throwTodo() instead of CompilerError.invariant(). Todo errors cause graceful bailouts in production; Invariant errors are hard failures indicating unexpected/invalid states.

// Unsupported but expected pattern - graceful bailout
CompilerError.throwTodo({
  reason: `Support [description of unsupported feature]`,
  loc: terminal.loc,
});

// Invariant is for truly unexpected/invalid states - hard failure
CompilerError.invariant(false, {
  reason: `Unexpected [thing]`,
  loc: terminal.loc,
});