Commit Graph
21924 Commits
Author SHA1 Message Date
Joseph Savona 519a62a113 Merge b0280210eb into sapling-pr-archive-josephsavona 2025-10-17 11:12:39 -07:00
Joe Savona b0280210eb [compiler] Optimize props spread for common cases
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.
2025-10-17 11:12:23 -07:00
Joe Savona 38b39be914 [compiler] More fbt compatibility
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.
2025-10-17 11:12:23 -07:00
Joseph Savona a4fd67ed0e Merge bf37b915bd into sapling-pr-archive-josephsavona 2025-10-17 10:50:23 -07:00
Joe Savona bf37b915bd [compiler] Optimize props spread for common cases
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.
2025-10-17 10:50:18 -07:00
Joseph Savona a783837543 Merge 6cc5951302 into sapling-pr-archive-josephsavona 2025-10-17 10:47:53 -07:00
Joe Savona 6cc5951302 [compiler] Optimize props spread for common cases
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.
2025-10-17 10:47:34 -07:00
Joseph Savona 1a16458db6 Merge a7afe44848 into sapling-pr-archive-josephsavona 2025-10-17 09:37:16 -07:00
Joe Savona a7afe44848 [compiler] More fbt compatibility
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.
2025-10-17 09:37:09 -07:00
Joseph Savona 13d75db429 Merge aa36cf4aea into sapling-pr-archive-josephsavona 2025-10-17 08:52:38 -07:00
Joe Savona aa36cf4aea [compiler] More fbt compatibility
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.
2025-10-17 08:52:29 -07:00
Joe Savona d0c1bede3e merge commit for archive created by Sapling 2025-10-17 08:49:53 -07:00
Joe Savona 08cbe39cc9 [compiler] More fbt compatibility
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.
2025-10-17 08:49:43 -07:00
Joe Savona 3a9718f5bf merge commit for archive created by Sapling 2025-10-16 21:43:00 -07:00
Joe Savona 46fa6c2eff [compiler] More fbt compatibility
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.
2025-10-16 21:42:47 -07:00
Joseph Savona 2dac63b51a Merge 1d413e4442 into sapling-pr-archive-josephsavona 2025-10-16 17:06:45 -07:00
Joe Savona 1d413e4442 [compiler] More fbt compatibility
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.
2025-10-16 17:06:35 -07:00
Joe Savona 3ec40e28f8 merge commit for archive created by Sapling 2025-10-16 17:05:18 -07:00
Joe Savona c1e0b0e4cb [compiler] More fbt compatibility
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.

Still WIP, ideally `fbt.plural()` and `fbt.param()` would be treated as shallow, but if we do that then PromoteUsedTemporaries can sometimes promote temporaries (@mofeiz called it) just because there are other scopes in between the fbt.plural operand and fbt.plural call.
2025-10-16 17:05:08 -07:00
Joe Savona 1c952577c7 merge commit for archive created by Sapling 2025-10-16 16:56:54 -07:00
Joe Savona 9714eabec0 [compiler] More fbt compatibility
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.

Still WIP, ideally `fbt.plural()` and `fbt.param()` would be treated as shallow, but if we do that then PromoteUsedTemporaries can sometimes promote temporaries (@mofeiz called it) just because there are other scopes in between the fbt.plural operand and fbt.plural call.
2025-10-16 16:56:44 -07:00
Joseph Savona ec3c426b02 Merge dd0dab534a into sapling-pr-archive-josephsavona 2025-10-16 16:48:15 -07:00
Joe Savona dd0dab534a [compiler] More fbt compatibility
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.

Still WIP, ideally `fbt.plural()` and `fbt.param()` would be treated as shallow, but if we do that then PromoteUsedTemporaries can sometimes promote temporaries (@mofeiz called it) just because there are other scopes in between the fbt.plural operand and fbt.plural call.
2025-10-16 16:48:03 -07:00
Joseph Savona 1324e1bb1f [compiler] Cleanup and enable validateNoVoidUseMemo (#34882)
This is a great validation, so let's enable by default. Changes:
* Move the validation logic into ValidateUseMemo alongside the new check
that the useMemo result is used
* Update the lint description
* Make the void memo errors lint-only, they don't require us to skip
compilation (as evidenced by the fact that we've had this validation
off)

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34882).
* #34855
* __->__ #34882
2025-10-16 13:08:57 -07:00
Joe Savona 1df7c23f2b merge commit for archive created by Sapling 2025-10-16 13:08:44 -07:00
Joe Savona 55bf051afd [compiler][wip] Fix for ValidatePreserveMemo edge case w multiple return in useMemo
Potential fix for #34750. With a useMemo with multiple returns we can sometimes end up thinking the FinishMemoize value wasn't memoized, even though it definitely is. But the same case works if you rewrite to have a let binding, assign to the let instead of returning, and then have a single return with the let — which is the exact shape we convert to when we inline the useMemo! The only difference is that when we inline, we end up with an extra LoadLocal. Adding an extra LoadLocal "fixes" the issue but this is definitely a hack.

However, along the way this helped me find that we also have a bug in PruneAlwaysInvalidatingScopes, where we were forgetting to set FinishMemoize instructions to pruned if their declaration always invalidates.

Putting up for feedback, not necessarily expecting to land as-is.
2025-10-16 13:08:23 -07:00
Joe Savona 2a18d35301 [compiler] Cleanup and enable validateNoVoidUseMemo
This is a great validation, so let's enable by default. Changes:
* Move the validation logic into ValidateUseMemo alongside the new check that the useMemo result is used
* Update the lint description
* Make the void memo errors lint-only, they don't require us to skip compilation (as evidenced by the fact that we've had this validation off)
2025-10-16 13:08:23 -07:00
Joseph Savona 7f5ea1bf67 [compiler] More useMemo validation (#34868)
Two additional validations for useMemo:
* Disallow reassigning to values declared outside the useMemo callback
(always on)
* Disallow unused useMemo calls (part of the validateNoVoidUseMemo
feature flag, which in turn is off by default)

We should probably enable this flag though!

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34868).
* #34855
* #34882
* __->__ #34868
2025-10-16 13:05:18 -07:00
Joseph Savona 24772a49d7 Merge a16291f1d6 into sapling-pr-archive-josephsavona 2025-10-16 12:57:28 -07:00
Joe Savona a16291f1d6 [compiler][wip] Fix for ValidatePreserveMemo edge case w multiple return in useMemo
Potential fix for #34750. With a useMemo with multiple returns we can sometimes end up thinking the FinishMemoize value wasn't memoized, even though it definitely is. But the same case works if you rewrite to have a let binding, assign to the let instead of returning, and then have a single return with the let — which is the exact shape we convert to when we inline the useMemo! The only difference is that when we inline, we end up with an extra LoadLocal. Adding an extra LoadLocal "fixes" the issue but this is definitely a hack.

However, along the way this helped me find that we also have a bug in PruneAlwaysInvalidatingScopes, where we were forgetting to set FinishMemoize instructions to pruned if their declaration always invalidates.

Putting up for feedback, not necessarily expecting to land as-is.
2025-10-16 12:57:15 -07:00
Joe Savona 7f6930cd7a [compiler] Cleanup and enable validateNoVoidUseMemo
This is a great validation, so let's enable by default. Changes:
* Move the validation logic into ValidateUseMemo alongside the new check that the useMemo result is used
* Update the lint description
* Make the void memo errors lint-only, they don't require us to skip compilation (as evidenced by the fact that we've had this validation off)
2025-10-16 12:57:13 -07:00
Joseph Savona a8a77824e6 Merge 38008218d5 into sapling-pr-archive-josephsavona 2025-10-16 12:28:02 -07:00
Joe Savona 38008218d5 [compiler][wip] Fix for ValidatePreserveMemo edge case w multiple return in useMemo
Potential fix for #34750. With a useMemo with multiple returns we can sometimes end up thinking the FinishMemoize value wasn't memoized, even though it definitely is. But the same case works if you rewrite to have a let binding, assign to the let instead of returning, and then have a single return with the let — which is the exact shape we convert to when we inline the useMemo! The only difference is that when we inline, we end up with an extra LoadLocal. Adding an extra LoadLocal "fixes" the issue but this is definitely a hack.

However, along the way this helped me find that we also have a bug in PruneAlwaysInvalidatingScopes, where we were forgetting to set FinishMemoize instructions to pruned if their declaration always invalidates.

Putting up for feedback, not necessarily expecting to land as-is.
2025-10-16 12:27:49 -07:00
Joe Savona 69ebf93ef2 [compiler] Cleanup and enable validateNoVoidUseMemo
This is a great validation, so let's enable by default. Changes:
* Move the validation logic into ValidateUseMemo alongside the new check that the useMemo result is used
* Update the lint description
* Make the void memo errors lint-only, they don't require us to skip compilation (as evidenced by the fact that we've had this validation off)
2025-10-16 12:27:48 -07:00
Damjan Petrovic 0e32da71c7 Add MIT license header to feature flag utility script (#34833)
Added the standard Meta Platforms, Inc. MIT license notice to the top of
the feature flag comparison script to ensure compliance with repository
licensing requirements and for code consistency.
**No functional or logic changes were made to the code.**
2025-10-16 14:20:21 -04:00
João Eirinha 2381ecc290 [ESLint] Disallow passing effect event down when inlined as a prop (#34820)
## Summary

Fixes https://github.com/facebook/react/issues/34793.

We are allowing passing down effect events when they are inlined as a
prop.

```
<Child onClick={useEffectEvent(...)} />
```

This seems like a case that someone not familiar with `useEffectEvent`'s
purpose could fall for so this PR introduces logic to disallow its
usage.

An alternative implementation would be to modify the name and function
of `recordAllUseEffectEventFunctions` to record all `useEffectEvent`
instances either assigned to a variable or not, but this seems clearer.
Or we could also specifically disallow its usage inside JSX. Feel free
to suggest any improvements.

## How did you test this change?

- Added a new test in
`packages/eslint-plugin-react-hooks/__tests__/ESLintRulesOfHooks-test.js`.
All tests pass.
2025-10-16 14:18:01 -04:00
Joseph Savona 76ed2f9861 Merge 64e2484339 into sapling-pr-archive-josephsavona 2025-10-16 10:54:18 -07:00
Joe Savona 64e2484339 [compiler][wip] Fix for ValidatePreserveMemo edge case w multiple return in useMemo
Potential fix for #34750. With a useMemo with multiple returns we can sometimes end up thinking the FinishMemoize value wasn't memoized, even though it definitely is. But the same case works if you rewrite to have a let binding, assign to the let instead of returning, and then have a single return with the let — which is the exact shape we convert to when we inline the useMemo! The only difference is that when we inline, we end up with an extra LoadLocal. Adding an extra LoadLocal "fixes" the issue but this is definitely a hack.

However, along the way this helped me find that we also have a bug in PruneAlwaysInvalidatingScopes, where we were forgetting to set FinishMemoize instructions to pruned if their declaration always invalidates.

Putting up for feedback, not necessarily expecting to land as-is.
2025-10-16 10:54:10 -07:00
Joe Savona a59c9fc6bf [compiler] More useMemo validation
Two additional validations for useMemo:
* Disallow reassigning to values declared outside the useMemo callback (always on)
* Disallow unused useMemo calls (part of the validateNoVoidUseMemo feature flag, which in turn is off by default)

We should probably enable this flag though!
2025-10-16 10:54:09 -07:00
Joseph Savona 272c9e54b0 Merge e791552bd5 into sapling-pr-archive-josephsavona 2025-10-16 10:43:17 -07:00
Joe Savona e791552bd5 [compiler][wip] Fix for ValidatePreserveMemo edge case w multiple return in useMemo
Potential fix for #34750. With a useMemo with multiple returns we can sometimes end up thinking the FinishMemoize value wasn't memoized, even though it definitely is. But the same case works if you rewrite to have a let binding, assign to the let instead of returning, and then have a single return with the let — which is the exact shape we convert to when we inline the useMemo! The only difference is that when we inline, we end up with an extra LoadLocal. Adding an extra LoadLocal "fixes" the issue but this is definitely a hack.

However, along the way this helped me find that we also have a bug in PruneAlwaysInvalidatingScopes, where we were forgetting to set FinishMemoize instructions to pruned if their declaration always invalidates.

Putting up for feedback, not necessarily expecting to land as-is.
2025-10-16 10:43:06 -07:00
Joe Savona d79680bfa1 [compiler] More useMemo validation
Two additional validations for useMemo:
* Disallow reassigning to values declared outside the useMemo callback (always on)
* Disallow unused useMemo calls (part of the validateNoVoidUseMemo feature flag, which in turn is off by default)

We should probably enable this flag though!
2025-10-16 10:43:04 -07:00
Ricky 5418d8bdc1 Fix changelog link (#34879)
Closes https://github.com/reactjs/react.dev/issues/8081
2025-10-16 13:40:26 -04:00
Henry Q. DineenandHenry Q. Dineen ed1351c4fb [compiler] improve zod v3 backwards compat (#34877)
## Summary

When upgrading to `babel-plugin-react-compiler@1.0.0` in a project that
uses `zod@3` we are running into TypeScript errors like:

```
node_modules/babel-plugin-react-compiler/dist/index.d.ts:435:10 - error TS2694: Namespace '"/REDACTED/node_modules/zod/v3/external"' has no exported member 'core'.

435     }, z.core.$strip>>>;
             ~~~~
```

This problem seems to be related to
d6eb735938, which introduced zod v3/v4
compatibility. Since `zod` is bundled into the compiler source this does
not cause runtime issues and only manifests as TypeScript errors. My
proposed solution is this PR is to use zod's [subpath versioning
strategy](https://zod.dev/v4/versioning?id=versioning-in-zod-4) which
allows you to support v3 and v4 APIs on both major versions.

Changes in this PR include:

- Updated `zod` import paths to `zod/v4`
- Bumped min `zod` version to `^3.25.0` for zod which guarantees the
`zod/v4` subpath is available.
- Updated `zod-validation-error` import paths to
`zod-validation-error/v4`
- Bumped min `zod-validation-error ` version to `^3.5.0` 
- Updated `externals` tsup configuration where appropriate. 

Once the compiler drops zod v3 support we could optionally remove the
`/v4` subpath from the imports.

## How did you test this change?

Not totally sure the best way to test. I ran `NODE_ENV=production yarn
workspace babel-plugin-react-compiler run build --dts` and diffed the
`dist/` folder between my change and `v1.0.0` and it looks correct. We
have a `patch-package` patch to workaround this for now and it works as
expected.

```diff
diff --git a/node_modules/babel-plugin-react-compiler/dist/index.d.ts b/node_modules/babel-plugin-react-compiler/dist/index.d.ts
index 81c3f3d..daafc2c 100644
--- a/node_modules/babel-plugin-react-compiler/dist/index.d.ts
+++ b/node_modules/babel-plugin-react-compiler/dist/index.d.ts
@@ -1,7 +1,7 @@
 import * as BabelCore from '@babel/core';
 import { NodePath as NodePath$1 } from '@babel/core';
 import * as t from '@babel/types';
-import { z } from 'zod';
+import { z } from 'zod/v4';
 import { NodePath, Scope } from '@babel/traverse';
 
 interface Result<T, E> {
```

Co-authored-by: Henry Q. Dineen <henryqdineen@gmail.com>
2025-10-16 09:46:55 -07:00
Sebastian Markbåge 93f8593289 [DevTools] Adjust the rects size by one pixel smaller (#34876)
This ensures that the outline of a previous rectangle lines up on the
same pixel as the next rectangle so that they appear consecutive.

<img width="244" height="51" alt="Screenshot 2025-10-16 at 11 35 32 AM"
src="https://github.com/user-attachments/assets/75ffde6f-8cc6-49c1-8855-3953569546b4"
/>

I don't love this implementation. There's probably a smarter way. Was
trying to avoid adding another element.
2025-10-16 12:16:16 -04:00
Sebastian Markbåge dc1becd893 [DevTools] Remove steps title from scrubber (#34878)
The hover now has a reach tooltip for the "environment" instead.
2025-10-16 12:16:04 -04:00
Sebastian "Sebbie" Silbermann d8aa94b0f4 Only capture stacks for up to 10 frames for Owner Stacks (#34864) 2025-10-16 18:00:41 +02:00
Sebastian Markbåge 03ba0c76e1 [DevTools] Include some sub-pixel precision in rects (#34873)
Currently the sub-pixel precision is lost which can lead to things not
lining up properly and being slightly off or overlapping.

We need some sub-pixel precision.

Ideally we'd just keep the floating point as is. I'm not sure why the
operations is limited to integers. We don't send it as a typed array
anyway it seems which would ideally be more optimal. Even if we did, we
haven't defined a precision for the protocol. Is it 32bit integer?
64bit? If it's 64bit we can fit a float anyway. Ideally it would be more
variable precision like just pushing into a typed array directly with
the option to write whatever precision we want.
2025-10-16 10:50:41 -04:00
Sebastian Markbåge 4e00747378 [DevTools] Don't pluralize if already plural (#34870)
In a demo today, `cookies()` showed up as `cookieses`. While adorable,
is wrong.
2025-10-16 10:50:18 -04:00
Sebastian Markbåge 7bd8716acd [DevTools] Don't try to load anonymous or empty urls (#34869)
This triggers unnecessary fetches.
2025-10-16 10:49:37 -04:00