Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: facebook/react
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 1324e1bb
Choose a base ref
...
head repository: facebook/react
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 58bdc0bb
Choose a head ref
  • 13 commits
  • 50 files changed
  • 4 contributors

Commits on Oct 17, 2025

  1. [compiler] More fbt compatibility (#34887)

    In my previous PR I fixed some cases but broke others. So, new approach.
    Two phase algorithm:
    
    * First pass is forward data flow to determine all usages of macros.
    This is necessary because many of Meta's macros have variants that can
    be accessed via properties, eg you can do `macro(...)` but also
    `macro.variant(...)`.
    * Second pass is backwards data flow to find macro invocations (JSX and
    calls) and then merge their operands into the same scope as the macro
    call.
    
    Note that this required updating PromoteUsedTemporaries to avoid
    promoting macro calls that have interposing instructions between their
    creation and usage. Macro calls in general are pure so it should be safe
    to reorder them.
    
    In addition, we're now more precise about `<fb:plural>`, `<fbt:param>`,
    `fbt.plural()` and `fbt.param()`, which don't actually require all their
    arguments to be inlined. The whole point is that the plural/param value
    is an arbitrary value (along with a string name). So we no longer
    transitively inline the arguments, we just make sure that they don't get
    inadvertently promoted to named variables.
    
    One caveat: we actually don't do anything to treat macro functions as
    non-mutating, so `fbt.plural()` and friends (function form) may still
    sometimes group arguments just due to mutability inference. In a
    follow-up, i'll work to infer the types of nested macro functions as
    non-mutating.
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34887).
    * #34900
    * __->__ #34887
    josephsavona authored Oct 17, 2025
    Configuration menu
    Copy the full SHA
    adbc32d View commit details
    Browse the repository at this point in the history
  2. [compiler] Optimize props spread for common cases (#34900)

    As part of the new inference model we updated to (correctly) treat
    destructuring spread as creating a new mutable object. This had the
    unfortunate side-effect of reducing precision on destructuring of props,
    though:
    
    ```js
    function Component({x, ...rest}) {
      const z = rest.z;
      identity(z);
      return <Stringify x={x} z={z} />;
    }
    ```
    
    Memoized as the following, where we don't realize that `z` is actually
    frozen:
    
    ```js
    function Component(t0) {
      const $ = _c(6);
      let x;
      let z;
      if ($[0] !== t0) {
        const { x: t1, ...rest } = t0;
        x = t1;
        z = rest.z;
        identity(z);
    ...
    ```
    
    #34341 was our first thought of how to do this (thanks @poteto for
    exploring this idea!). But during review it became clear that it was a
    bit more complicated than I had thought. So this PR explores a more
    conservative alternative. The idea is:
    
    * Track known sources of frozen values: component props, hook params,
    and hook return values.
    * Find all object spreads where the rvalue is a known frozen value.
    * Look at how such objects are used, and if they are only used to access
    properties (PropertyLoad/Destructure), pass to hooks, or pass to jsx
    then we can be very confident the object is not mutated. We consider any
    such objects to be frozen, even though technically spread creates a new
    object.
    
    See new fixtures for more examples.
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34900).
    * __->__ #34900
    * #34887
    josephsavona authored Oct 17, 2025
    Configuration menu
    Copy the full SHA
    c35f6a3 View commit details
    Browse the repository at this point in the history
  3. [Flight] Fix detached ArrayBuffer error when streaming typed arrays (

    …#34849)
    
    Using `renderToReadableStream` in Node.js with binary data from
    `fs.readFileSync` (or `Buffer.allocUnsafe`) could cause downstream
    consumers (like compression middleware) to fail with "Cannot perform
    Construct on a detached ArrayBuffer".
    
    The issue occurs because Node.js uses an 8192-byte Buffer pool for small
    allocations (< 4KB). When React's `VIEW_SIZE` was 2KB, files between
    ~2KB and 4KB would be passed through as views of pooled buffers rather
    than copied into `currentView`. ByteStreams (`type: 'bytes'`) detach
    ArrayBuffers during transfer, which corrupts the shared Buffer pool and
    causes subsequent Buffer operations to fail.
    
    Increasing `VIEW_SIZE` from 2KB to 4KB ensures all chunks smaller than
    4KB are copied into `currentView` (which uses a dedicated 4KB buffer
    outside the pool), while chunks 4KB or larger don't use the pool anyway.
    Thus no pooled buffers are ever exposed to ByteStream detachment.
    
    This adds 2KB memory per active stream, copies chunks in the 2-4KB range
    instead of passing them as views (small CPU cost), and buffers up to 2KB
    more data before flushing. However, it avoids duplicating large binary
    data (which copying everything would require, like the Edge entry point
    currently does in `typedArrayToBinaryChunk`).
    
    Related issues:
    
    - vercel/next.js#84753
    - vercel/next.js#84858
    unstubbable authored Oct 17, 2025
    Configuration menu
    Copy the full SHA
    dc485c7 View commit details
    Browse the repository at this point in the history
  4. [DevTools] Tweak the rects design and create multi-environment color …

    …scheme (#34880)
    
    <img width="1011" height="811" alt="Screenshot 2025-10-16 at 2 20 46 PM"
    src="https://github.com/user-attachments/assets/6dea3962-d369-4823-b44f-2c62b566c8f1"
    />
    
    The selection is now clearer with a wider outline which spans the
    bounding box if there are multi rects.
    
    The color now gets darked changes on hover with a slight animation.
    
    The colors are now mixed from constants defined which are consistently
    used in the rects, the time span in the "suspended by" side bar and the
    scrubber. I also have constants defined for "server" and "other" debug
    environments which will be used in a follow up.
    sebmarkbage authored Oct 17, 2025
    Configuration menu
    Copy the full SHA
    ef88c58 View commit details
    Browse the repository at this point in the history
  5. [DevTools] Repeat the "name" if there's no short description in groups (

    #34894)
    
    It looks weird when the row is blank when there's no short description
    for the entry in a group.
    
    <img width="328" height="436" alt="Screenshot 2025-10-17 at 12 25 30 AM"
    src="https://github.com/user-attachments/assets/12f5c55f-a37f-4b6d-913e-f763cec6b211"
    />
    sebmarkbage authored Oct 17, 2025
    Configuration menu
    Copy the full SHA
    724e7bf View commit details
    Browse the repository at this point in the history
  6. [DevTools] Highlight the rect when the corresponding timeline bean is…

    … hovered (#34881)
    
    Stacked on #34880.
    
    In #34861 I removed the highlight of the real view when hovering the
    timeline since it was disruptive to stepping through the visuals.
    
    This makes it so that when we hover the timeline we highlight the rect
    with the subtle hover effect added in #34880.
    
    We can now just use the one shared state for this and don't need the CSS
    psuedo-selectors.
    
    <img width="603" height="813" alt="Screenshot 2025-10-16 at 3 11 17 PM"
    src="https://github.com/user-attachments/assets/a018b5ce-dd4d-4e77-ad47-b4ea068f1976"
    />
    sebmarkbage authored Oct 17, 2025
    Configuration menu
    Copy the full SHA
    f970d5f View commit details
    Browse the repository at this point in the history
  7. [DevTools] Don't highlight the root rect if no roots has unique suspe…

    …nders (#34885)
    
    Stacked on #34881.
    
    We don't paint suspense boundaries if there are no suspenders. This does
    the same with the root. The root is still selectable so you can confirm
    but there's no affordance drawing attention to click the root.
    
    This could happen if you don't use the built-ins of React to load things
    like scripts and css. It would never happen in something like Next.js
    where code and CSS is loaded through React-native like RSC.
    
    However, it could also happen in the Activity scoped case when all
    resources are always loaded early.
    sebmarkbage authored Oct 17, 2025
    Configuration menu
    Copy the full SHA
    423c44b View commit details
    Browse the repository at this point in the history
  8. [DevTools] Compute environment names for the timeline (#34892)

    Stacked on #34885.
    
    This refactors the timeline to store not just an id but a complex object
    for each step. This will later represent a group of boundaries.
    
    Each timeline step is assigned an environment name. We pick the last
    environment name (assumed to have resolved last) from the union of the
    parent and child environment names. I.e. a child step is considered to
    be blocked by the parent so if a child isn't blocked on any environment
    name it still gets marked as the parent's environment name.
    
    In a follow up, I'd like to reorder the document order timeline based on
    environment names to favor loading everything in one environment before
    the next.
    sebmarkbage authored Oct 17, 2025
    Configuration menu
    Copy the full SHA
    a083344 View commit details
    Browse the repository at this point in the history
  9. [DevTools] Assign a different color and label based on environment (#…

    …34893)
    
    Stacked on #34892.
    
    In the timeline scrubber each timeline entry gets a label and color
    assigned based on the environment computed for that step.
    
    In the rects, we find the timeline step that this boundary is part of
    and use that environment to assign a color. This is slightly different
    than picking from the boundary itself since it takes into account parent
    boundaries.
    
    In the "suspended by" section we color each entry individually based on
    the environment that spawned the I/O.
    
    <img width="790" height="813" alt="Screenshot 2025-10-17 at 12 18 56 AM"
    src="https://github.com/user-attachments/assets/c902b1fb-0992-4e24-8e94-a97ca8507551"
    />
    sebmarkbage authored Oct 17, 2025
    Configuration menu
    Copy the full SHA
    3a66917 View commit details
    Browse the repository at this point in the history

Commits on Oct 18, 2025

  1. Configuration menu
    Copy the full SHA
    40c7a7f View commit details
    Browse the repository at this point in the history

Commits on Oct 19, 2025

  1. Resolve the .default export of a React.lazy as the canonical value (#…

    …34906)
    
    For debug purposes this is the value that the `React.lazy` resolves to.
    It also lets us look at that value for descriptions like its name.
    sebmarkbage authored Oct 19, 2025
    Configuration menu
    Copy the full SHA
    ec7d9a7 View commit details
    Browse the repository at this point in the history
  2. [DevTools] Infer name from stack if it's the generic "lazy" name (#34907

    )
    
    Stacked on #34906.
    
    Infer name from stack if it's the generic "lazy" name. It might be
    wrapped in an abstraction. E.g. `next/dynamic`.
    
    Also use the function name as a description of a resolved function
    value.
    
    <img width="310" height="166" alt="Screenshot 2025-10-18 at 10 42 05 AM"
    src="https://github.com/user-attachments/assets/c63170b9-2b19-4f30-be7a-6429bb3ef3d9"
    />
    sebmarkbage authored Oct 19, 2025
    Configuration menu
    Copy the full SHA
    bf11d2f View commit details
    Browse the repository at this point in the history
  3. [Flight] Ignore bound-anonymous-fn resources as they're not considere…

    …d I/O (#34911)
    
    When you create a snapshot from an AsyncLocalStorage in Node.js, that
    creates a new bound AsyncResource which everything runs inside of.
    
    
    https://github.com/nodejs/node/blob/3437e1c4bd529e51d96ea581b6435bbeb77ef524/lib/internal/async_local_storage/async_hooks.js#L61-L67
    
    This resource is itself tracked by our async debug tracking as I/O. We
    can't really distinguish these in general from other AsyncResources
    which are I/O.
    
    However, by default they're given the name `"bound-anonymous-fn"` if you
    pass it an anonymous function or in the case of a snapshot, that's
    built-in:
    
    
    https://github.com/nodejs/node/blob/3437e1c4bd529e51d96ea581b6435bbeb77ef524/lib/async_hooks.js#L262-L263
    
    We can at least assume that these are non-I/O. If you want to ensure
    that a bound resource is not considered I/O, you can ensure your
    function isn't assigned a name or give it this explicit name.
    
    The other issue here is that, the sequencing here is that we track the
    callsite of the `.snapshot()` or `.bind()` call as the trigger. So if
    that was outside of render for example, then it would be considered
    non-I/O. However, this might miss stuff if you resolve promises inside
    the `.run()` of the snapshot if the `.run()` call itself was spawned by
    I/O which should be tracked. Time will tell if those patterns appear.
    However, in cases like nested renders (e.g. Next.js's "use cache") then
    restoring it as if it was outside the parent render is what you do want.
    sebmarkbage authored Oct 19, 2025
    Configuration menu
    Copy the full SHA
    58bdc0b View commit details
    Browse the repository at this point in the history
Loading