forked from facebook/react
-
Notifications
You must be signed in to change notification settings - Fork 0
Mirror of upstream PR #35292 #611
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
kushxg
wants to merge
867
commits into
main
Choose a base branch
from
upstream-pr-35292
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This change allows it so that tabs that were open before a compiler error are automatically opened again when the error is resolved. Quality of life change for those especially working with the advanced view of the playground. https://github.com/user-attachments/assets/cd2dc117-e6fc-4f57-a08f-259757c4f5e8
The View Transition docs were unclear about this but apparently the `finished` promise never settles if the animation never started. So if there's an error that rejects the `ready` promise, we'll never run the clean up which can cause it to stall. Fixes facebook#34662. However, ultimately that is caused by Chrome stalling our default `onDefaultTransitionIndicator` but it should be unblocked after 10 seconds, not a minute.
…ine profiler is not supported (facebook#34684)
…ebook#34503) The `@enablePreserveExistingMemoizationGuarantees` mode can still fail to preserve manual memoization due to mismtached dependencies. Specifically, where the user's dependencies are more precise than the compiler infers bc the compiler is being conservative about what might be nullable. In this mode though we're intentionally using information from the manual memoization and can also rely on the deps as a signal for what's non-nullable. The idea of the PR is that we treat manual memo deps just like other inferred-as-non-nullable objects during PropagateScopeDeps. We're careful to not treat the full path as non-nullable, only up to the last property index. So `x.y.z` as a manual dep treats `x` and `x.y` as non-nullable, allowing us to preserve a conditional dependency on `x.y.z`. Optionals within manual dependencies are a bit trickier and aren't handled yet, but hopefully that's less common and something we can improve in a follow-up. Not handling them just means that developers may hit false positives on validating existing memoization if they use optional chains in manual dependencies. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34503). * facebook#34689 * __->__ facebook#34503
…lt (facebook#34689) This enables `@enablePreserveExistingMemoizationGuarantees` by default. As of the previous PR (facebook#34503), this mode now enables the following behaviors: - Treating variables referenced within a `useMemo()` or `useCallback()` as "frozen" (immutable) as of the start of the call. Ie, the compiler will assume that the values you reference are not mutated by the body of the useMemo, not are they mutated later. Directly modifying them (eg `var.property = true`) will be an error. - Similarly, the results of the useMemo/useCallback are treated as frozen (immutable) after the call. These two rules match the behavior for other hooks: this means that developers will see similar behavior to swapping out `useMemo()` for a custom `useMyMemo()` wrapper/alias. Additionally, as of facebook#34503 the compiler uses information from the manual dependencies to know which variables are non-nullable. Even if a useMemo block conditionally accesses a nested property — `if (cond) { log(x.y.z) }` — where the compiler would not usually know that `x` is non-nullable, if the user specifies `x.y.z` as a manual dependency then the compiler knows that `x` and `x.y` are non-nullable and can infer a more precise dependency. Finally, this mode also ensures that we always memoize function calls that return primitives. See facebook#34343 for more details. For now, I've explicitly opted out of this feature in all test fixtures where the behavior changed.
…efault (facebook#34654) Rebased on facebook#34454. Always include the root in the timeline even if it has no unique suspenders, since even if it won't suspend, we have to be able to see that and step to one step before the next boundary to see the first boundary that does suspend in its fallback state. Also, if there's no current selection on initial mount, select the last entry in the timeline. We usually do this with `selectedSuspenseID` but that doesn't happen on initial load. So this does it on initial load if nothing else is selected by then. That way when you reload you get the initial root selected. There's a problem here because we should really use one source of truth and `selectedSuspenseID` doesn't really do anything now. Either it should be its separate source of truth and you can't show components in the side-panel or it should be derived from the other state. If it's derived, once there's a selection, e.g. in the root, then even if new timelines load it will never change but that's probably a good thing.
Stacked on facebook#34654. The root is special since it represents "Initial Paint" (or a "Transition" when an Activity is selected). This gives it a different color in the timeline as well as gives it an outline that's clickable. Hovering the timeline now shows "Initial Paint" or "Suspense". Also made the cursor a pointer to invite you to try to click things and some rounded corners. <img width="1219" height="420" alt="Screenshot 2025-10-02 at 1 26 38 PM" src="https://github.com/user-attachments/assets/12451f93-8917-4f3b-8f01-930129e5fc13" /> <img width="1217" height="419" alt="Screenshot 2025-10-02 at 1 26 54 PM" src="https://github.com/user-attachments/assets/02b5e94c-3fbe-488d-b0f2-225b73578608" /> <img width="1215" height="419" alt="Screenshot 2025-10-02 at 1 27 24 PM" src="https://github.com/user-attachments/assets/c24e8861-e74a-4ccc-8643-ee9d04bef43c" /> <img width="1216" height="419" alt="Screenshot 2025-10-02 at 1 27 10 PM" src="https://github.com/user-attachments/assets/d5cc2b62-fa64-41bf-b485-116b1cd67467" />
…#34630) We're showing too much noise in the side-panel when selecting a Suspense boundary. The interesting thing to see directly is the "Suspended by". The "props" are mostly useless because the `"name"` prop is already in the tree. I'm now also showing it in the title bar of the selected element panel. The "children" and "fallback" props are just the thing that you can see in the tree view anyway. The "state" is this weird section with just one field in it, which we already have duplicated in the top toolbar as well. We can just delete this. I make sure to show the icon and a "suspended..." section while the boundary is still loading but now yet resuspended by force suspending. While still loading: <img width="600" height="193" alt="Screenshot 2025-09-27 at 11 54 37 PM" src="https://github.com/user-attachments/assets/1c3f3a96-46e0-4b11-806f-032569c7d5b5" /> After loading: <img width="602" height="266" alt="Screenshot 2025-09-27 at 11 54 53 PM" src="https://github.com/user-attachments/assets/c43cc4cb-036f-4ced-9b0d-226c6320cd76" /> Resuspended after loading: <img width="602" height="300" alt="Screenshot 2025-09-27 at 11 55 07 PM" src="https://github.com/user-attachments/assets/0be01735-48a7-47dc-b5cf-e72ec71e0148" />
…acebook#34695) Somehow my last commit didn't make it in facebook#34630.
…k#34694) When we flush a Suspense boundary we might not flush the fallback segment, it might only flush a placeholder instead. In this case the segment can flush again but we do not want to flush the boundary itself a second time. We now detach the boundary after flushing it. better solution to: facebook#34668
…e on (facebook#34698) This auto updates to select the last entry in the timeline until we make the first selection. That way when new content loads in, we show the last timeline of what is visible.
The previous DiffEditor view of the playground looked broken and not cohesive. There would be parts of the scrollbar appearing on the left side for some reason, along with two scrollbars on the right side. This PR makes the DiffEditor look more cohesive. Previous: https://github.com/user-attachments/assets/1aa1c775-5940-43b2-a75a-9b46452fb78b After: https://github.com/user-attachments/assets/b5c04998-6a6c-4b52-b3c5-b2fef21729e0
Adds back the compiler rules to the recommended preset, intended for the next release. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34675). * facebook#34703 * facebook#34700 * facebook#34699 * __->__ facebook#34675
Updates the eslint fixture lockfiles. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34699). * facebook#34703 * facebook#34700 * __->__ facebook#34699 * facebook#34675
…ok#34700) Previously, the `recommended` config used the legacy ESLint format (plugins as an array of strings). This causes errors when used with ESLint v9's `defineConfig()` helper. This was following [eslint's own docs](https://eslint.org/docs/latest/extend/plugins#backwards-compatibility-for-legacy-configs): > With this approach, both configuration systems recognize "recommended". The old config system uses the recommended key while the current config system uses the flat/recommended key. The defineConfig() helper first looks at the recommended key, and if that is not in the correct format, it looks for the flat/recommended key. This allows you an upgrade path if you’d later like to rename flat/recommended to recommended when you no longer need to support the old config system. However, [`isLegacyConfig()`](https://github.com/eslint/rewrite/blob/main/packages/config-helpers/src/define-config.js#L73-L81) (also see [`eslintrcKeys`](https://github.com/eslint/rewrite/blob/main/packages/config-helpers/src/define-config.js#L24-L35)) function doesn't check for the `plugins` key, so our config was incorrectly treated as flat config despite being in legacy format. This PR fixes the issue, along with a few other fixes combined: 1. Convert `recommended` to flat config format 2. Separate basic rules (exhaustive-deps, rules-of-hooks) from compiler rules 3. Add `recommended-latest-legacy` config for non-flat config users who want all recommended rules (including compiler rules) 4. Adding more types for the exported config Our shipped presets in 6.x.x will essentially be: - `recommended-legacy`: legacy (non-flat), with basic rules only - `recommended-latest-legacy`: legacy (non-flat), all rules (basic + compiler) - `flat/recommended`: flat, basic rules only (now the same as recommended, but to avoid making a breaking change we'll just keep it around in 6.x.x) - `recommended-latest`: flat, all rules (basic + compiler) - `recommended`: flat, basic rules only In the next breaking release 7.x.x, we will collapse down the presets into three: - `recommended-legacy`: all recommended rules - `recommended`: all recommended rules - `recommended-experimental`: all recommended rules + new bleeding edge experimental rules Closes facebook#34679 --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34700). * facebook#34703 * __->__ facebook#34700
This rule was a leftover from a while ago and doesn't actually lint anything useful. Specifically, you get a lint error if you try to opt out a component that isn't already bailing out. If there's a bailout the compiler already safely skips over it, so adding `'use no memo'` there is unnecessary. Fixes facebook#31407 --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34703). * __->__ facebook#34703 * facebook#34700
Stacked on facebook#34533 for root fragment handling This is the same approach as DOM, where we call getRootNode on the parent. Tests are in react-native using Fantom.
Stacked on facebook#34544 We only have getBoundingClientRect available from RN currently. This should work as a substitute for this case because the equivalent of multi-rect elements in RN is a nested Text component. We only include the rects of top-level host components here so we can assume that calling getBoundingClientRect on each child is the same result. Tested in react-native with Fantom.
Fixed two small issues with the config panel in the compiler playground: 1. Object descriptions were being confined in the config box and most of it would not be visible upon hover 2. Changed it so that "Applied Configs" would only display a valid set of configs, rather than switching between "Invalid Configs" and the set of options. This would be less visually jarring for users as the Output panel already displays errors. Additionally, if users want to see the list of config options but have a currently broken config, they would previously not know how to fix it. Object hover before: <img width="702" height="481" alt="Screenshot 2025-09-26 at 10 41 03 AM" src="https://github.com/user-attachments/assets/b2ddec2f-16ba-41a1-be1f-96211f46764c" /> Hover after: <img width="702" height="481" alt="Screenshot 2025-09-26 at 10 40 37 AM" src="https://github.com/user-attachments/assets/dc713a22-4710-46a8-a5d7-485060cc9074" /> Applied Configs always displays the last valid set of configs: https://github.com/user-attachments/assets/2fb9232f-7388-4488-9b7a-bb48bf09e4ca
…he timeline (facebook#34704) Unlike the rects, this never toggles. It just jumps.
) redo of facebook#34458 but fixing up prettier Co-authored-by: Arnaud Barré <[email protected]>
We will be focusing eslint-plugin-react-hooks as the primary OSS-only package for our lint plugin. eslint-plugin-react-compiler will remain as a Meta only package as some limitations of our internal infra require us to use packages that aren't widely adopted by the rest of the industry. This PR removes `hermes-parser`, which was meant to support parsing Flow syntax.
Partial redo of facebook#34710. The changes there tried to use `z.function(args, return)` to be compatible across Zod v3 and v4, but Zod 4's function API has completely changed. Instead, I've updated to just use `z.any()` where we expect a function, and manually validate that it's a function before we call the value. We already have validation of the return type (also using Zod). Co-authored-by: kolvian <[email protected]>
…acebook#35184) With `ValidateExhaustiveMemoDependencies` we can now check exhaustive dependencies for useMemo and useCallback within the compiler, without relying on the separate exhaustive-deps rule. Until now we've bailed out of any component/hook that suppresses this rule, since the suppression _might_ affect a memoization value. Compiling code with incorrect memo deps can change behavior so this wasn't safe. The downside was that a suppression within a useEffect could prevent memoization, even though non-exhaustive deps for effects do not cause problems for memoization specifically. So here, we change to ignore ESLint suppressions if we have both the compiler's hooks validation and memo deps validations enabled. Now we just have to test out the new validation and refine before we can enable this by default. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/35184). * facebook#35201 * facebook#35202 * facebook#35192 * facebook#35190 * facebook#35186 * facebook#35185 * __->__ facebook#35184
…k#35185) When checking ValidateExhaustiveDeps internally, this seems to be the most common case that it flags. The current exhaustive-deps rule allows extraneous deps if they are a set of stable types. So here we reuse our existing isStableType() util in the compiler to allow this case. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/35185). * facebook#35201 * facebook#35202 * facebook#35192 * facebook#35190 * facebook#35186 * __->__ facebook#35185
…an inferred deps (facebook#35186) Since adding this validation we've already changed our inference to use knowledge from manual memoization to inform when values are frozen and which values are non-nullable. To align with that, if the user chooses to use different optionality btw the deps and the memo block/callback, that's fine. The key is that eg `x?.y` will invalidate whenever `x.y` would, so from a memoization correctness perspective its fine. It's not our job to be a type checker: if a value is potentially nullable, it should likely use a nullable property access in both places but TypeScript/Flow can check that. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/35186). * facebook#35201 * facebook#35202 * facebook#35192 * facebook#35190 * __->__ facebook#35186
The existing exhaustive-deps rule allows omitting non-reactive dependencies, even if they're not memoized. Conceptually, if a value is non-reactive then it cannot semantically change. Even if the value is a new object, that object represents the exact same value and doesn't necessitate redoing downstream computation. Thus its fine to exclude nonreactive dependencies, whether they're a stable type or not. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/35190). * facebook#35201 * facebook#35202 * facebook#35192 * __->__ facebook#35190
…rule (facebook#35192) Similar to ValidateHookUsage, we implement this check in the compiler for safety but (for now) continue to rely on the existing rule for actually reporting errors to users. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/35192). * facebook#35201 * facebook#35202 * __->__ facebook#35192
…cebook#35202) --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/35202). * facebook#35201 * __->__ facebook#35202
In ValidateExhaustiveDependencies, I previously changed to allow extraneous dependencies as long as they were non-reactive. Here we make that more precise, and distinguish between values that are definitely referenced in the memo function but optional as dependencies vs values that are not even referenced in the memo function. The latter now error as extraneous even if they're non-reactive. This also turned up a case where constant-folded primitives could show up as false positives of the latter category, so now we track manual deps which quality for constant folding and don't error on them. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/35204). * facebook#35213 * facebook#35201 * __->__ facebook#35204
facebook#35201) Enables `@validateExhaustiveMemoizationDependencies` feature flag by default, and disables it in select tests that failed due to the change. Some of our tests intentionally use incorrect memo dependencies in order to test edge cases. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/35201). * facebook#35213 * __->__ facebook#35201
…#35213) First, this adds some more tests and organizes them into an `exhaustive-deps/` subdirectory. Second, the diagnostics are overhauled. For each memo block we now report a single diagnostic which summarizes the issue, plus individual errors for each missing/extra dependency. Within the extra deps, we distinguish whether it's truly extra vs whether its just a more (too) precise version of an inferred dep. For example, if you depend on `x.y.z` but the inferred dep was `x.y`. Finally, we print the full inferred deps at the end as a hint (it's also a suggestion, but this makes it more clear what would be suggested).
…ix (facebook#35215) Fixes some issues i ran into w my recent snap changes: * Correctly match against patterns that contain subdirectories, eg `fbt/fbt-call` * When checking if the input pattern has an extension, only prune known supported extensions. Our convention of `error.<name>` for fixtures that error makes the rest of the test name look like an extension to `path.extname()`. Tested with lots of different patterns including `error.` examples at the top level and in nested directories, etc.
…cebook#35214) ValidateNoSetStateInEffects already supports transitive setter functions. This PR marks any synchonous state setter useEffectEvent function so we can validate that uEE isn't being used only as misdirection to avoid the validation within an effect body. The error points to the call of the effect event. Example: ```js export default function MyApp() { const [count, setCount] = useState(0) const effectEvent = useEffectEvent(() => { setCount(10) }) useEffect(() => { effectEvent() }, []) return <div>{count}</div>; ``` ``` Found 1 error: Error: Calling setState synchronously within an effect can trigger cascading renders Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: * Update external systems with the latest state from React. * Subscribe for updates from some external system, calling setState in a callback function when external state changes. Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). 5 | }) 6 | useEffect(() => { > 7 | effectEvent() | ^^^^^^^^^^^ Avoid calling setState() directly within an effect 8 | }, []) 9 | return <div>{count}</div>; 10 | } ```
…5277) FlightReplyServer are for client->server and ReactFlightClient is for server->client. They're not 100% symmetrical. We did a number of refactors to ReactFlightClient in PRs like facebook#29823 and facebook#33664 to change the structure of the resolution. This PR brings those changes to synchronize the two approaches. Which addresses deep resolution of cycles and deferred error handling. This also fixes a critical security vulnerability.
…ebook#35258) ## Summary To help people access the documentation easier, we can [add `meta.docs.url`](https://eslint.org/docs/latest/extend/custom-rules#:~:text=Specifies%20the%20URL) to the new react-compiler rules. This allows IDEs to make the rule name a clickable link. ## How did you test this change? `yarn test`, `yarn prettier`, `yarn lint` and in a separate project [using file:// URLs](https://stackoverflow.com/a/38417065)
…r errors (facebook#35230) Adds a new `enableUseKeyedState` compiler flag that changes the error message for unconditional setState calls during render. When `enableUseKeyedState` is enabled, the error recommends using `useKeyedState(initialState, key)` to reset state when dependencies change. When disabled (the default), it links to the React docs for the manual pattern of storing previous values in state. Both error messages now include helpful bullet points explaining the two main alternatives: 1. Use useKeyedState (or manual pattern) to reset state when other state/props change 2. Compute derived data directly during render without using state
AFAIK this is not needed to prevent any exploit but we don't really need this. We allow functions on pretty much any other object anyway but never on the "then" property since those would be serialized as Promises by the client anyway.
…t-server-dom-unbundled` (facebook#35290) Co-authored-by: Hendrik Liebau <[email protected]>
Follow-up to facebook#34641. Similar to facebook#35293, facebook#35294. React DevTools backend can be used in non-DOM environments, so we have to feature-check some DOM APIs. For now I am just no-oping newly added commands for Native, we should revisit this decision once we would roll out Suspense panel there, if needed. I am not sure if scrolling will be required as much as it is needed on Web. `isReactNativeEnvironment()` check is kinda clowny, but we've been relying on it for quite some time already.
…ook#35284) Fixes an edge case where a function expression would fail to take a dependency if it referenced a hoisted `const` inferred as a primitive value. We were incorrectly skipping primitve-typed operands when determing scopes for merging in InferReactiveScopeVariables. This was super tricky to debug, for posterity the trick is that Context variables (StoreContext etc) are modeled just like a mutable object, where assignment to the variable is equivalent to `object.value = ...` and reading the variable is equivalent to `object.value` property access. Comparing to an equivalent version of the repro case replaced with an object and property read/writes showed that everything was exactly right, except that InferReactiveScopeVariables wasn't merging the scopes of the function and the context variable, which led me right to the problematic line. Closes facebook#35122
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Mirrored from facebook/react PR facebook#35292