From 62af6b22803d743ff5101f89be6f088ecfd46b00 Mon Sep 17 00:00:00 2001 From: Jan Kassens Date: Wed, 12 Jun 2024 11:12:22 -0400 Subject: [PATCH 01/11] delete ReactServerStreamConfig.dom-fb-experimental.js (#29836) delete ReactServerStreamConfig.dom-fb-experimental.js This config is no longer used since 1cd77a4ff7a2189003965246a3cfc475d2d9857d --- ...tServerStreamConfig.dom-fb-experimental.js | 99 ------------------- 1 file changed, 99 deletions(-) delete mode 100644 packages/react-server/src/forks/ReactServerStreamConfig.dom-fb-experimental.js diff --git a/packages/react-server/src/forks/ReactServerStreamConfig.dom-fb-experimental.js b/packages/react-server/src/forks/ReactServerStreamConfig.dom-fb-experimental.js deleted file mode 100644 index 2d705e2a1c..0000000000 --- a/packages/react-server/src/forks/ReactServerStreamConfig.dom-fb-experimental.js +++ /dev/null @@ -1,99 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - */ - -export * from '../ReactServerStreamConfigFB'; - -import type { - PrecomputedChunk, - Chunk, - BinaryChunk, -} from '../ReactServerStreamConfigFB'; - -let byteLengthImpl: null | ((chunk: Chunk | PrecomputedChunk) => number) = null; - -export function setByteLengthOfChunkImplementation( - impl: (chunk: Chunk | PrecomputedChunk) => number, -): void { - byteLengthImpl = impl; -} - -export function byteLengthOfChunk(chunk: Chunk | PrecomputedChunk): number { - if (byteLengthImpl == null) { - // eslint-disable-next-line react-internal/prod-error-codes - throw new Error( - 'byteLengthOfChunk implementation is not configured. Please, provide the implementation via ReactFlightDOMServer.setConfig(...);', - ); - } - return byteLengthImpl(chunk); -} - -export interface Destination { - beginWriting(): void; - write(chunk: Chunk | PrecomputedChunk | BinaryChunk): void; - completeWriting(): void; - flushBuffered(): void; - close(): void; - onError(error: mixed): void; -} - -function handleErrorInNextTick(error: any) { - setTimeout(() => { - throw error; - }); -} - -const LocalPromise = Promise; - -/** - * Since this environment doesn't have a way to schedule tasks from JS we schedule - * using a microtask instead. This isn't necessarily ideal since we would like to give - * other IO a chance to run before performing work typically but it's the best we can - * do in this environment - */ -export function scheduleWork(callback: () => void) { - LocalPromise.resolve().then(callback).catch(handleErrorInNextTick); -} - -export const scheduleMicrotask: (callback: () => void) => void = scheduleWork; - -export function beginWriting(destination: Destination) { - destination.beginWriting(); -} - -export function writeChunk( - destination: Destination, - chunk: Chunk | PrecomputedChunk | BinaryChunk, -): void { - destination.write(chunk); -} - -export function writeChunkAndReturn( - destination: Destination, - chunk: Chunk | PrecomputedChunk | BinaryChunk, -): boolean { - destination.write(chunk); - return true; -} - -export function completeWriting(destination: Destination) { - destination.completeWriting(); -} - -export function flushBuffered(destination: Destination) { - destination.flushBuffered(); -} - -export function close(destination: Destination) { - destination.close(); -} - -export function closeWithError(destination: Destination, error: mixed): void { - destination.onError(error); - destination.close(); -} From 93826c8483ffaf6676c437c25619d82f13444413 Mon Sep 17 00:00:00 2001 From: Jan Kassens Date: Wed, 12 Jun 2024 11:14:51 -0400 Subject: [PATCH 02/11] remove unstable_renderSubtreeIntoContainer (#29771) remove unstable_renderSubtreeIntoContainer This is finally no longer used and can be deleted. --- packages/react-dom/src/ReactDOMFB.js | 1 - .../renderSubtreeIntoContainer-test.js | 353 ------------------ .../react-dom/src/client/ReactDOMRootFB.js | 41 -- .../src/ReactFiberClassComponent.js | 22 +- packages/shared/ReactInstanceMap.js | 13 - 5 files changed, 1 insertion(+), 429 deletions(-) delete mode 100644 packages/react-dom/src/__tests__/renderSubtreeIntoContainer-test.js diff --git a/packages/react-dom/src/ReactDOMFB.js b/packages/react-dom/src/ReactDOMFB.js index 409aa376d5..4825c80dfb 100644 --- a/packages/react-dom/src/ReactDOMFB.js +++ b/packages/react-dom/src/ReactDOMFB.js @@ -43,6 +43,5 @@ export { render, unstable_batchedUpdates, findDOMNode, - unstable_renderSubtreeIntoContainer, unmountComponentAtNode, } from './client/ReactDOMRootFB'; diff --git a/packages/react-dom/src/__tests__/renderSubtreeIntoContainer-test.js b/packages/react-dom/src/__tests__/renderSubtreeIntoContainer-test.js deleted file mode 100644 index 4f93cd3ac8..0000000000 --- a/packages/react-dom/src/__tests__/renderSubtreeIntoContainer-test.js +++ /dev/null @@ -1,353 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @emails react-core - */ - -'use strict'; - -const React = require('react'); -const PropTypes = require('prop-types'); -const ReactDOM = require('react-dom'); -const ReactDOMClient = require('react-dom/client'); -const act = require('internal-test-utils').act; -const renderSubtreeIntoContainer = - require('react-dom').unstable_renderSubtreeIntoContainer; - -describe('renderSubtreeIntoContainer', () => { - // @gate !disableLegacyContext - // @gate !disableLegacyMode - it('should pass context when rendering subtree elsewhere', () => { - const portal = document.createElement('div'); - - class Component extends React.Component { - static contextTypes = { - foo: PropTypes.string.isRequired, - }; - - render() { - return
{this.context.foo}
; - } - } - - class Parent extends React.Component { - static childContextTypes = { - foo: PropTypes.string.isRequired, - }; - - getChildContext() { - return { - foo: 'bar', - }; - } - - render() { - return null; - } - - componentDidMount() { - expect( - function () { - renderSubtreeIntoContainer(this, , portal); - }.bind(this), - ).toErrorDev( - 'ReactDOM.unstable_renderSubtreeIntoContainer() has not been supported since React 18', - ); - } - } - - const container = document.createElement('div'); - ReactDOM.render(, container); - expect(portal.firstChild.innerHTML).toBe('bar'); - }); - - // @gate !disableLegacyContext - // @gate !disableLegacyMode - it('should update context if it changes due to setState', async () => { - const container = document.createElement('div'); - document.body.appendChild(container); - const portal = document.createElement('div'); - - class Component extends React.Component { - static contextTypes = { - foo: PropTypes.string.isRequired, - getFoo: PropTypes.func.isRequired, - }; - - render() { - return
{this.context.foo + '-' + this.context.getFoo()}
; - } - } - - class Parent extends React.Component { - static childContextTypes = { - foo: PropTypes.string.isRequired, - getFoo: PropTypes.func.isRequired, - }; - - state = { - bar: 'initial', - }; - - getChildContext() { - return { - foo: this.state.bar, - getFoo: () => this.state.bar, - }; - } - - render() { - return null; - } - - componentDidMount() { - expect(() => { - renderSubtreeIntoContainer(this, , portal); - }).toErrorDev( - 'ReactDOM.unstable_renderSubtreeIntoContainer() has not been supported since React 18', - ); - } - - componentDidUpdate() { - expect(() => { - renderSubtreeIntoContainer(this, , portal); - }).toErrorDev( - 'ReactDOM.unstable_renderSubtreeIntoContainer() has not been supported since React 18', - ); - } - } - const root = ReactDOMClient.createRoot(container); - const parentRef = React.createRef(); - await act(async () => { - root.render(); - }); - const instance = parentRef.current; - - expect(portal.firstChild.innerHTML).toBe('initial-initial'); - await act(async () => { - instance.setState({bar: 'changed'}); - }); - expect(portal.firstChild.innerHTML).toBe('changed-changed'); - }); - - // @gate !disableLegacyContext - // @gate !disableLegacyMode - it('should update context if it changes due to re-render', async () => { - const container = document.createElement('div'); - document.body.appendChild(container); - const portal = document.createElement('div'); - - class Component extends React.Component { - static contextTypes = { - foo: PropTypes.string.isRequired, - getFoo: PropTypes.func.isRequired, - }; - - render() { - return
{this.context.foo + '-' + this.context.getFoo()}
; - } - } - - class Parent extends React.Component { - static childContextTypes = { - foo: PropTypes.string.isRequired, - getFoo: PropTypes.func.isRequired, - }; - - getChildContext() { - return { - foo: this.props.bar, - getFoo: () => this.props.bar, - }; - } - - render() { - return null; - } - - componentDidMount() { - expect(() => { - renderSubtreeIntoContainer(this, , portal); - }).toErrorDev( - 'ReactDOM.unstable_renderSubtreeIntoContainer() has not been supported since React 18', - ); - } - - componentDidUpdate() { - expect(() => { - renderSubtreeIntoContainer(this, , portal); - }).toErrorDev( - 'ReactDOM.unstable_renderSubtreeIntoContainer() has not been supported since React 18', - ); - } - } - - const root = ReactDOMClient.createRoot(container); - await act(async () => { - root.render(); - }); - expect(portal.firstChild.innerHTML).toBe('initial-initial'); - await act(async () => { - root.render(); - }); - expect(portal.firstChild.innerHTML).toBe('changed-changed'); - }); - - // @gate !disableLegacyMode - it('should render portal with non-context-provider parent', async () => { - const container = document.createElement('div'); - document.body.appendChild(container); - const portal = document.createElement('div'); - - class Parent extends React.Component { - render() { - return null; - } - - componentDidMount() { - expect(() => { - renderSubtreeIntoContainer(this,
hello
, portal); - }).toErrorDev( - 'ReactDOM.unstable_renderSubtreeIntoContainer() has not been supported since React 18', - ); - } - } - - const root = ReactDOMClient.createRoot(container); - await act(async () => { - root.render(); - }); - expect(portal.firstChild.innerHTML).toBe('hello'); - }); - - // @gate !disableLegacyContext - // @gate !disableLegacyMode - it('should get context through non-context-provider parent', async () => { - const container = document.createElement('div'); - document.body.appendChild(container); - const portal = document.createElement('div'); - - class Parent extends React.Component { - render() { - return ; - } - getChildContext() { - return {value: this.props.value}; - } - static childContextTypes = { - value: PropTypes.string.isRequired, - }; - } - - class Middle extends React.Component { - render() { - return null; - } - componentDidMount() { - expect(() => { - renderSubtreeIntoContainer(this, , portal); - }).toErrorDev( - 'ReactDOM.unstable_renderSubtreeIntoContainer() has not been supported since React 18', - ); - } - } - - class Child extends React.Component { - static contextTypes = { - value: PropTypes.string.isRequired, - }; - render() { - return
{this.context.value}
; - } - } - - const root = ReactDOMClient.createRoot(container); - await act(async () => { - root.render(); - }); - expect(portal.textContent).toBe('foo'); - }); - - // @gate !disableLegacyContext - // @gate !disableLegacyMode - it('should get context through middle non-context-provider layer', async () => { - const container = document.createElement('div'); - document.body.appendChild(container); - const portal1 = document.createElement('div'); - const portal2 = document.createElement('div'); - - class Parent extends React.Component { - render() { - return null; - } - getChildContext() { - return {value: this.props.value}; - } - componentDidMount() { - expect(() => { - renderSubtreeIntoContainer(this, , portal1); - }).toErrorDev( - 'ReactDOM.unstable_renderSubtreeIntoContainer() has not been supported since React 18', - ); - } - static childContextTypes = { - value: PropTypes.string.isRequired, - }; - } - - class Middle extends React.Component { - render() { - return null; - } - componentDidMount() { - expect(() => { - renderSubtreeIntoContainer(this, , portal2); - }).toErrorDev( - 'ReactDOM.unstable_renderSubtreeIntoContainer() has not been supported since React 18', - ); - } - } - - class Child extends React.Component { - static contextTypes = { - value: PropTypes.string.isRequired, - }; - render() { - return
{this.context.value}
; - } - } - - const root = ReactDOMClient.createRoot(container); - await act(async () => { - root.render(); - }); - expect(portal2.textContent).toBe('foo'); - }); - - // @gate !disableLegacyMode - it('legacy test: fails gracefully when mixing React 15 and 16', () => { - class C extends React.Component { - render() { - return
; - } - } - const c = ReactDOM.render(, document.createElement('div')); - // React 15 calls this: - // https://github.com/facebook/react/blob/77b71fc3c4/src/renderers/dom/client/ReactMount.js#L478-L479 - expect(() => { - c._reactInternalInstance._processChildContext({}); - }).toThrow( - __DEV__ - ? '_processChildContext is not available in React 16+. This likely ' + - 'means you have multiple copies of React and are attempting to nest ' + - 'a React 15 tree inside a React 16 tree using ' + - "unstable_renderSubtreeIntoContainer, which isn't supported. Try to " + - 'make sure you have only one copy of React (and ideally, switch to ' + - 'ReactDOM.createPortal).' - : "Cannot read property '_processChildContext' of undefined", - ); - }); -}); diff --git a/packages/react-dom/src/client/ReactDOMRootFB.js b/packages/react-dom/src/client/ReactDOMRootFB.js index 64a1cf12ac..1e56097266 100644 --- a/packages/react-dom/src/client/ReactDOMRootFB.js +++ b/packages/react-dom/src/client/ReactDOMRootFB.js @@ -59,7 +59,6 @@ import { } from 'react-reconciler/src/ReactFiberReconciler'; import {LegacyRoot} from 'react-reconciler/src/ReactRootTags'; import getComponentNameFromType from 'shared/getComponentNameFromType'; -import {has as hasInstance} from 'shared/ReactInstanceMap'; import { current as currentOwner, @@ -420,46 +419,6 @@ export function render( ); } -export function unstable_renderSubtreeIntoContainer( - parentComponent: React$Component, - element: React$Element, - containerNode: Container, - callback: ?Function, -): React$Component | PublicInstance | null { - if (disableLegacyMode) { - if (__DEV__) { - console.error( - 'ReactDOM.unstable_renderSubtreeIntoContainer() was removed in React 19. Consider using a portal instead.', - ); - } - throw new Error('ReactDOM: Unsupported Legacy Mode API.'); - } - if (__DEV__) { - console.error( - 'ReactDOM.unstable_renderSubtreeIntoContainer() has not been supported ' + - 'since React 18. Consider using a portal instead. Until you switch to ' + - "the createRoot API, your app will behave as if it's running React " + - '17. Learn more: https://react.dev/link/switch-to-createroot', - ); - } - - if (!isValidContainerLegacy(containerNode)) { - throw new Error('Target container is not a DOM element.'); - } - - if (parentComponent == null || !hasInstance(parentComponent)) { - throw new Error('parentComponent must be a valid React Component'); - } - - return legacyRenderSubtreeIntoContainer( - parentComponent, - element, - containerNode, - false, - callback, - ); -} - export function unmountComponentAtNode(container: Container): boolean { if (disableLegacyMode) { if (__DEV__) { diff --git a/packages/react-reconciler/src/ReactFiberClassComponent.js b/packages/react-reconciler/src/ReactFiberClassComponent.js index 6bf04da4b4..5a7692949b 100644 --- a/packages/react-reconciler/src/ReactFiberClassComponent.js +++ b/packages/react-reconciler/src/ReactFiberClassComponent.js @@ -73,9 +73,7 @@ import { setIsStrictModeForDevtools, } from './ReactFiberDevToolsHook'; -const fakeInternalInstance: { - _processChildContext?: () => empty, -} = {}; +const fakeInternalInstance = {}; let didWarnAboutStateAssignmentForComponent; let didWarnAboutUninitializedState; @@ -98,24 +96,6 @@ if (__DEV__) { didWarnAboutInvalidateContextType = new Set(); didWarnOnInvalidCallback = new Set(); - // This is so gross but it's at least non-critical and can be removed if - // it causes problems. This is meant to give a nicer error message for - // ReactDOM15.unstable_renderSubtreeIntoContainer(reactDOM16Component, - // ...)) which otherwise throws a "_processChildContext is not a function" - // exception. - Object.defineProperty(fakeInternalInstance, '_processChildContext', { - enumerable: false, - value: function (): empty { - throw new Error( - '_processChildContext is not available in React 16+. This likely ' + - 'means you have multiple copies of React and are attempting to nest ' + - 'a React 15 tree inside a React 16 tree using ' + - "unstable_renderSubtreeIntoContainer, which isn't supported. Try " + - 'to make sure you have only one copy of React (and ideally, switch ' + - 'to ReactDOM.createPortal).', - ); - }, - }); Object.freeze(fakeInternalInstance); } diff --git a/packages/shared/ReactInstanceMap.js b/packages/shared/ReactInstanceMap.js index 7eab16f55b..2ad235c7a5 100644 --- a/packages/shared/ReactInstanceMap.js +++ b/packages/shared/ReactInstanceMap.js @@ -15,23 +15,10 @@ * If this becomes an actual Map, that will break. */ -/** - * This API should be called `delete` but we'd have to make sure to always - * transform these to strings for IE support. When this transform is fully - * supported we can rename it. - */ -export function remove(key) { - key._reactInternals = undefined; -} - export function get(key) { return key._reactInternals; } -export function has(key) { - return key._reactInternals !== undefined; -} - export function set(key, value) { key._reactInternals = value; } From f3e09d6328eb0eca53d8dbc19ea6f8f4aa43db25 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 Jun 2024 11:17:06 -0400 Subject: [PATCH 03/11] Bump braces from 3.0.2 to 3.0.3 in /fixtures/flight-esm (#29857) Bumps [braces](https://github.com/micromatch/braces) from 3.0.2 to 3.0.3.
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=braces&package-manager=npm_and_yarn&previous-version=3.0.2&new-version=3.0.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/facebook/react/network/alerts).
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- fixtures/flight-esm/yarn.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/fixtures/flight-esm/yarn.lock b/fixtures/flight-esm/yarn.lock index a00d5c244d..8d336e5194 100644 --- a/fixtures/flight-esm/yarn.lock +++ b/fixtures/flight-esm/yarn.lock @@ -79,11 +79,11 @@ brace-expansion@^1.1.7: concat-map "0.0.1" braces@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== dependencies: - fill-range "^7.0.1" + fill-range "^7.1.1" browserslist@^4.18.1: version "4.21.7" @@ -265,10 +265,10 @@ escalade@^3.1.1: resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== dependencies: to-regex-range "^5.0.1" From 50e89ec9f2d44ab140e771e585226f7771da9652 Mon Sep 17 00:00:00 2001 From: Lenz Weber-Tronic Date: Wed, 12 Jun 2024 20:13:17 +0200 Subject: [PATCH 04/11] Avoid acccessing React internals from `use-sync-external-store/shim` (#29868) Co-authored-by: eps1lon --- .../useSyncExternalStoreShared-test.js | 58 +++++++------------ .../src/useSyncExternalStore.js | 5 +- .../src/useSyncExternalStoreShimClient.js | 10 +++- scripts/jest/TestFlags.js | 9 +-- 4 files changed, 36 insertions(+), 46 deletions(-) diff --git a/packages/use-sync-external-store/src/__tests__/useSyncExternalStoreShared-test.js b/packages/use-sync-external-store/src/__tests__/useSyncExternalStoreShared-test.js index 1ad3de60da..6f7232674a 100644 --- a/packages/use-sync-external-store/src/__tests__/useSyncExternalStoreShared-test.js +++ b/packages/use-sync-external-store/src/__tests__/useSyncExternalStoreShared-test.js @@ -20,7 +20,7 @@ let useState; let useEffect; let useLayoutEffect; let assertLog; -let originalError; +let assertConsoleErrorDev; // This tests shared behavior between the built-in and shim implementations of // of useSyncExternalStore. @@ -50,9 +50,6 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => { : 'react-dom-17/umd/react-dom.production.min.js', ), ); - // Because React 17 prints extra logs we need to ignore them. - originalError = console.error; - console.error = jest.fn(); } React = require('react'); ReactDOM = require('react-dom'); @@ -63,6 +60,7 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => { useLayoutEffect = React.useLayoutEffect; const InternalTestUtils = require('internal-test-utils'); assertLog = InternalTestUtils.assertLog; + assertConsoleErrorDev = InternalTestUtils.assertConsoleErrorDev; const internalAct = require('internal-test-utils').act; // The internal act implementation doesn't batch updates by default, since @@ -85,11 +83,6 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => { useSyncExternalStoreWithSelector = require('use-sync-external-store/shim/with-selector').useSyncExternalStoreWithSelector; }); - afterEach(() => { - if (gate(flags => flags.enableUseSyncExternalStoreShim)) { - console.error = originalError; - } - }); function Text({text}) { Scheduler.log(text); return text; @@ -630,36 +623,30 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => { const container = document.createElement('div'); const root = createRoot(container); await expect(async () => { - await expect(async () => { - await act(() => { - ReactDOM.flushSync(async () => - root.render(React.createElement(App, null)), - ); - }); - }).rejects.toThrow( - 'Maximum update depth exceeded. This can happen when a component repeatedly ' + - 'calls setState inside componentWillUpdate or componentDidUpdate. React limits ' + - 'the number of nested updates to prevent infinite loops.', - ); - }).toErrorDev( + await act(() => { + ReactDOM.flushSync(async () => + root.render(React.createElement(App, null)), + ); + }); + }).rejects.toThrow( + 'Maximum update depth exceeded. This can happen when a component repeatedly ' + + 'calls setState inside componentWillUpdate or componentDidUpdate. React limits ' + + 'the number of nested updates to prevent infinite loops.', + ); + + assertConsoleErrorDev( gate(flags => flags.enableUseSyncExternalStoreShim) ? [ - 'Maximum update depth exceeded. ', - 'The result of getSnapshot should be cached to avoid an infinite loop', - 'The above error occurred in the', + [ + 'The result of getSnapshot should be cached to avoid an infinite loop', + {withoutStack: true}, + ], + 'Error: Maximum update depth exceeded', + 'The above error occurred i', ] : [ 'The result of getSnapshot should be cached to avoid an infinite loop', ], - { - withoutStack: gate(flags => { - if (flags.enableUseSyncExternalStoreShim) { - // Stacks don't work when mixing the source and the npm package. - return flags.source ? 1 : 0; - } - return false; - }), - }, ); }); it('getSnapshot can return NaN without infinite loop warning', async () => { @@ -850,10 +837,9 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => { // client. To avoid this server mismatch warning, user must account for // this themselves and return the correct value inside `getSnapshot`. await act(() => { - expect(() => - ReactDOM.hydrate(React.createElement(App, null), container), - ).toErrorDev('Text content did not match'); + ReactDOM.hydrate(React.createElement(App, null), container); }); + assertConsoleErrorDev(['Text content did not match']); assertLog(['client', 'Passive effect: client']); } expect(container.textContent).toEqual('client'); diff --git a/packages/use-sync-external-store/src/useSyncExternalStore.js b/packages/use-sync-external-store/src/useSyncExternalStore.js index ba8e62499a..8d6c49105d 100644 --- a/packages/use-sync-external-store/src/useSyncExternalStore.js +++ b/packages/use-sync-external-store/src/useSyncExternalStore.js @@ -16,7 +16,10 @@ import * as React from 'react'; export const useSyncExternalStore = React.useSyncExternalStore; if (__DEV__) { - console.error( + // Avoid transforming the `console.error` call as it would cause the built artifact + // to access React internals, which exist under different paths depending on the + // React version. + console['error']( "The main 'use-sync-external-store' entry point is not supported; all it " + "does is re-export useSyncExternalStore from the 'react' package, so " + 'it only works with React 18+.' + diff --git a/packages/use-sync-external-store/src/useSyncExternalStoreShimClient.js b/packages/use-sync-external-store/src/useSyncExternalStoreShimClient.js index 2bda9f11bf..579933f3dd 100644 --- a/packages/use-sync-external-store/src/useSyncExternalStoreShimClient.js +++ b/packages/use-sync-external-store/src/useSyncExternalStoreShimClient.js @@ -40,7 +40,10 @@ export function useSyncExternalStore( if (!didWarnOld18Alpha) { if (React.startTransition !== undefined) { didWarnOld18Alpha = true; - console.error( + // Avoid transforming the `console.error` call as it would cause the built artifact + // to access React internals, which exist under different paths depending on the + // React version. + console['error']( 'You are using an outdated, pre-release alpha of React 18 that ' + 'does not support useSyncExternalStore. The ' + 'use-sync-external-store shim will not work correctly. Upgrade ' + @@ -59,7 +62,10 @@ export function useSyncExternalStore( if (!didWarnUncachedGetSnapshot) { const cachedValue = getSnapshot(); if (!is(value, cachedValue)) { - console.error( + // Avoid transforming the `console.error` call as it would cause the built artifact + // to access React internals, which exist under different paths depending on the + // React version. + console['error']( 'The result of getSnapshot should be cached to avoid an infinite loop', ); didWarnUncachedGetSnapshot = true; diff --git a/scripts/jest/TestFlags.js b/scripts/jest/TestFlags.js index 0434529ab8..4e81149f2a 100644 --- a/scripts/jest/TestFlags.js +++ b/scripts/jest/TestFlags.js @@ -95,13 +95,8 @@ function getTestFlags() { // This is used by useSyncExternalStoresShared-test.js to decide whether // to test the shim or the native implementation of useSES. - // TODO: It's disabled when enableRefAsProp is on because the JSX - // runtime used by our tests is not compatible with older versions of - // React. If we want to keep testing this shim after enableRefIsProp is - // on everywhere, we'll need to find some other workaround. Maybe by - // only using createElement instead of JSX in that test module. - enableUseSyncExternalStoreShim: - !__VARIANT__ && !featureFlags.enableRefAsProp, + + enableUseSyncExternalStoreShim: !__VARIANT__, // If there's a naming conflict between scheduler and React feature flags, the // React ones take precedence. From 195d5bb99e366889f0905779a0f9432d1624f999 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= Date: Wed, 12 Jun 2024 16:15:22 -0400 Subject: [PATCH 05/11] Execute event handlers in the context of the instance that it's associated with (#29876) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit That way we get owner stacks (native or otherwise) for `console.error` or `console.warn` inside of them. Since the `reportError` is also called within this context, we also get them for errors thrown within event listeners. You'll also be able to observe this in in the `error` event. Similar to how `onUncaughtError` is in the scope of the instance that errored - even though `onUncaughtError` doesn't kick in for event listeners. Chrome (from console.createTask): Screenshot 2024-06-12 at 2 08 19 PM Screenshot 2024-06-12 at 2 03 32 PM Firefox (from React DevTools): Screenshot 2024-06-12 at 2 05 01 PM (This is the parent stack since React DevTools doesn't just yet print owner stack.) (Firefox doesn't print the component stack for uncaught since we don't add component stacks for "error" events from React DevTools - just console.error. Perhaps an oversight.) If we didn't have the synthetic event system this would kind of just work natively in Chrome because we have this task active when we attach the event listeners to the DOM node and async stacks just follow along that way. In fact, if you attach a manual listener in useEffect you get this same effect. It's just because we use event delegation that this doesn't work. However, if we did get rid of the synthetic event system we'd likely still want to add a wrapper on the DOM node to set our internal current owner so that the non-native part of the system still can observe the active instance. That wouldn't work with manually attached listeners though. --- .../src/events/DOMPluginEventSystem.js | 27 +++++++++++++++++-- .../src/legacy-events/EventPluginUtils.js | 20 ++++++++++++-- 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/packages/react-dom-bindings/src/events/DOMPluginEventSystem.js b/packages/react-dom-bindings/src/events/DOMPluginEventSystem.js index fe007b6209..5c3c9bffa1 100644 --- a/packages/react-dom-bindings/src/events/DOMPluginEventSystem.js +++ b/packages/react-dom-bindings/src/events/DOMPluginEventSystem.js @@ -52,6 +52,7 @@ import { enableLegacyFBSupport, enableCreateEventHandleAPI, enableScopeAPI, + enableOwnerStacks, } from 'shared/ReactFeatureFlags'; import {createEventListenerWrapperWithPriority} from './ReactDOMEventListener'; import { @@ -70,6 +71,8 @@ import * as FormActionEventPlugin from './plugins/FormActionEventPlugin'; import reportGlobalError from 'shared/reportGlobalError'; +import {runWithFiberInDEV} from 'react-reconciler/src/ReactCurrentFiber'; + type DispatchListener = { instance: null | Fiber, listener: Function, @@ -255,7 +258,17 @@ function processDispatchQueueItemsInOrder( if (instance !== previousInstance && event.isPropagationStopped()) { return; } - executeDispatch(event, listener, currentTarget); + if (__DEV__ && enableOwnerStacks && instance !== null) { + runWithFiberInDEV( + instance, + executeDispatch, + event, + listener, + currentTarget, + ); + } else { + executeDispatch(event, listener, currentTarget); + } previousInstance = instance; } } else { @@ -264,7 +277,17 @@ function processDispatchQueueItemsInOrder( if (instance !== previousInstance && event.isPropagationStopped()) { return; } - executeDispatch(event, listener, currentTarget); + if (__DEV__ && enableOwnerStacks && instance !== null) { + runWithFiberInDEV( + instance, + executeDispatch, + event, + listener, + currentTarget, + ); + } else { + executeDispatch(event, listener, currentTarget); + } previousInstance = instance; } } diff --git a/packages/react-native-renderer/src/legacy-events/EventPluginUtils.js b/packages/react-native-renderer/src/legacy-events/EventPluginUtils.js index f97d7b5488..02444e079a 100644 --- a/packages/react-native-renderer/src/legacy-events/EventPluginUtils.js +++ b/packages/react-native-renderer/src/legacy-events/EventPluginUtils.js @@ -7,6 +7,10 @@ import isArray from 'shared/isArray'; +import {enableOwnerStacks} from 'shared/ReactFeatureFlags'; + +import {runWithFiberInDEV} from 'react-reconciler/src/ReactCurrentFiber'; + let hasError = false; let caughtError = null; @@ -93,10 +97,22 @@ export function executeDispatchesInOrder(event) { break; } // Listeners and Instances are two parallel arrays that are always in sync. - executeDispatch(event, dispatchListeners[i], dispatchInstances[i]); + const listener = dispatchListeners[i]; + const instance = dispatchInstances[i]; + if (__DEV__ && enableOwnerStacks && instance !== null) { + runWithFiberInDEV(instance, executeDispatch, event, listener, instance); + } else { + executeDispatch(event, listener, instance); + } } } else if (dispatchListeners) { - executeDispatch(event, dispatchListeners, dispatchInstances); + const listener = dispatchListeners; + const instance = dispatchInstances; + if (__DEV__ && enableOwnerStacks && instance !== null) { + runWithFiberInDEV(instance, executeDispatch, event, listener, instance); + } else { + executeDispatch(event, listener, instance); + } } event._dispatchListeners = null; event._dispatchInstances = null; From 55fdcf87bdab39853252369ad0dc68cb88a15102 Mon Sep 17 00:00:00 2001 From: Joe Savona Date: Wed, 12 Jun 2024 14:49:13 -0700 Subject: [PATCH 06/11] [compiler] Fix merging of queued states in InferReferenceEffects Fixes a bug found by mofeiZ in #29878. When we merge queued states, if the new state does not introduce changes relative to the queued state we should use the queued state, not the new state. ghstack-source-id: c59f69de15fa89bd1ed049d0a7d221651577ae34 Pull Request resolved: https://github.com/facebook/react/pull/29879 --- .../src/Inference/InferReferenceEffects.ts | 4 +- .../compiler/phi-reference-effects.expect.md | 61 +++++++++++++++++++ .../compiler/phi-reference-effects.ts | 19 ++++++ 3 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/phi-reference-effects.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/phi-reference-effects.ts diff --git a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts index 619d1d90ff..ee2ad1a7de 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts @@ -201,7 +201,7 @@ export default function inferReferenceEffects( let queuedState = queuedStates.get(blockId); if (queuedState != null) { // merge the queued states for this block - state = queuedState.merge(state) ?? state; + state = queuedState.merge(state) ?? queuedState; queuedStates.set(blockId, state); } else { /* @@ -765,7 +765,7 @@ class InferenceState { result.values[id] = { kind, value: printMixedHIR(value) }; } for (const [variable, values] of this.#variables) { - result.variables[variable] = [...values].map(identify); + result.variables[`$${variable}`] = [...values].map(identify); } return result; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/phi-reference-effects.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/phi-reference-effects.expect.md new file mode 100644 index 0000000000..bef1d7b836 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/phi-reference-effects.expect.md @@ -0,0 +1,61 @@ + +## Input + +```javascript +import { arrayPush } from "shared-runtime"; + +function Foo(cond) { + let x = null; + if (cond) { + x = []; + } else { + } + // Here, x = phi(x$null, x$[]) should receive a ValueKind of Mutable + arrayPush(x, 2); + + return x; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Foo, + params: [{ cond: true }], + sequentialRenders: [{ cond: true }, { cond: true }], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { arrayPush } from "shared-runtime"; + +function Foo(cond) { + const $ = _c(2); + let x; + if ($[0] !== cond) { + x = null; + if (cond) { + x = []; + } + + arrayPush(x, 2); + $[0] = cond; + $[1] = x; + } else { + x = $[1]; + } + return x; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Foo, + params: [{ cond: true }], + sequentialRenders: [{ cond: true }, { cond: true }], +}; + +``` + +### Eval output +(kind: ok) [2] +[2] \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/phi-reference-effects.ts b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/phi-reference-effects.ts new file mode 100644 index 0000000000..092791d586 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/phi-reference-effects.ts @@ -0,0 +1,19 @@ +import { arrayPush } from "shared-runtime"; + +function Foo(cond) { + let x = null; + if (cond) { + x = []; + } else { + } + // Here, x = phi(x$null, x$[]) should receive a ValueKind of Mutable + arrayPush(x, 2); + + return x; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Foo, + params: [{ cond: true }], + sequentialRenders: [{ cond: true }, { cond: true }], +}; From 7f3911fac01829646c5be5e1236b76555f598f36 Mon Sep 17 00:00:00 2001 From: Josh Story Date: Wed, 12 Jun 2024 15:31:23 -0700 Subject: [PATCH 07/11] Standardize condition order so that edge-lite preferred over browser (#29877) The export maps for react packages have to choose an order of preference. Many runtimes use multiple conditions, for instance when building for edge webpack also uses the browser condition which makes sense given most edge runtimes have a web-standards based set of APIs. However React is building the browser builds primarily for actual browsers and sometimes have builds intended for servers that might be browser compat. This change updates the order of conditions to preference specific named runtimes > node > generic edge runtimes > browser > default --- packages/react-dom/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-dom/package.json b/packages/react-dom/package.json index 170471ad8a..481548f374 100644 --- a/packages/react-dom/package.json +++ b/packages/react-dom/package.json @@ -63,9 +63,9 @@ "bun": "./server.bun.js", "deno": "./server.browser.js", "worker": "./server.browser.js", - "browser": "./server.browser.js", "node": "./server.node.js", "edge-light": "./server.edge.js", + "browser": "./server.browser.js", "default": "./server.node.js" }, "./server.browser": { @@ -89,9 +89,9 @@ "workerd": "./static.edge.js", "deno": "./static.browser.js", "worker": "./static.browser.js", - "browser": "./static.browser.js", "node": "./static.node.js", "edge-light": "./static.edge.js", + "browser": "./static.browser.js", "default": "./static.node.js" }, "./static.browser": { From 814a4186459eb79ed9bc6f22de4a4f75ff77558c Mon Sep 17 00:00:00 2001 From: Mike Vitousek Date: Wed, 12 Jun 2024 15:31:59 -0700 Subject: [PATCH 08/11] [compiler] Make unary and binary operator types more precise Summary: Minor change inspired by #29863: the BuildHIR pass ensures that Binary and UnaryOperator nodes only use a limited set of the operators that babel's operator types represent, which that pr relies on for safe reorderability, but the type of those HIR nodes admits the other operators. For example, even though you can't build an HIR UnaryOperator with `delete` as the operator, it is a valid HIR node--and if we made a mistaken change that let you build such a node, it would be unsafe to reorder. This pr makes the typing of operators stricter to prevent that. ghstack-source-id: 9bf3b1a37eae3f14c0e9fb42bb3ece522b317d98 Pull Request resolved: https://github.com/facebook/react/pull/29880 --- .../src/HIR/BuildHIR.ts | 27 ++++++++++++++++++- .../src/HIR/HIR.ts | 4 +-- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts index 826b720300..e5a067018d 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts @@ -1668,6 +1668,15 @@ function lowerExpression( const left = lowerExpressionToTemporary(builder, leftPath); const right = lowerExpressionToTemporary(builder, expr.get("right")); const operator = expr.node.operator; + if (operator === "|>") { + builder.errors.push({ + reason: `(BuildHIR::lowerExpression) Pipe operator not supported`, + severity: ErrorSeverity.Todo, + loc: leftPath.node.loc ?? null, + suggestions: null, + }); + return { kind: "UnsupportedNode", node: exprNode, loc: exprLoc }; + } return { kind: "BinaryExpression", operator, @@ -1893,7 +1902,9 @@ function lowerExpression( ); } - const operators: { [key: string]: t.BinaryExpression["operator"] } = { + const operators: { + [key: string]: Exclude">; + } = { "+=": "+", "-=": "-", "/=": "/", @@ -2307,6 +2318,20 @@ function lowerExpression( }); return { kind: "UnsupportedNode", node: expr.node, loc: exprLoc }; } + } else if (expr.node.operator === "throw") { + builder.errors.push({ + reason: `Throw expressions are not supported`, + severity: ErrorSeverity.InvalidJS, + loc: expr.node.loc ?? null, + suggestions: [ + { + description: "Remove this line", + range: [expr.node.start!, expr.node.end!], + op: CompilerSuggestionOperation.Remove, + }, + ], + }); + return { kind: "UnsupportedNode", node: expr.node, loc: exprLoc }; } else { return { kind: "UnaryExpression", diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts index d544269869..2294335034 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts @@ -866,7 +866,7 @@ export type InstructionValue = | JSXText | { kind: "BinaryExpression"; - operator: t.BinaryExpression["operator"]; + operator: Exclude">; left: Place; right: Place; loc: SourceLocation; @@ -881,7 +881,7 @@ export type InstructionValue = | MethodCall | { kind: "UnaryExpression"; - operator: t.UnaryExpression["operator"]; + operator: Exclude; value: Place; loc: SourceLocation; } From d9a5b6393a9329b60592e34c9e1fe091e6af5090 Mon Sep 17 00:00:00 2001 From: Vitali Zaidman Date: Thu, 13 Jun 2024 15:37:51 +0100 Subject: [PATCH 09/11] =?UTF-8?q?fix[react-devtools]=20divided=20inspectin?= =?UTF-8?q?g=20elements=20between=20inspecting=20do=E2=80=A6=20(#29885)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # **before** * nav to dom element from devtools * nav to devtools element from page are enabled on extension and disabled on the rest of the flavors. ## extension: * nav to dom element from devtools **enabled** and working * nav to devtools element from page **enabled** and working ![Screenshot 2024-06-13 at 11 15 11](https://github.com/facebook/react/assets/5188459/fef78b70-d22c-4405-8871-8b0449b51937) ## inline: * nav to dom element from devtools **disabled** * nav to devtools element from page **disabled** ![before-inline](https://github.com/facebook/react/assets/5188459/24020dc2-baec-4d0a-84d4-45c96d653843) ## standalone: * nav to dom element from devtools **disabled** * nav to devtools element from page **disabled** ![before-standalone](https://github.com/facebook/react/assets/5188459/19b4cb34-9d1f-412e-baea-59ea85f99d04) ## fusebox: * nav to dom element from devtools **disabled** * nav to devtools element from page **disabled** ![before-fusebox](https://github.com/facebook/react/assets/5188459/1a18fda4-04b8-40f4-ae8b-e059889fca93) # **after** same: * nav to dom element from devtools * nav to devtools element from page are enabled on extension and disabled on inline. change: standalone and fusebox can nav to devtools element from page ## extension: * nav to dom element from devtools **enabled** and working * nav to devtools element from page **enabled** and working ![Screenshot 2024-06-13 at 10 50 25](https://github.com/facebook/react/assets/5188459/f4679c72-b211-43d6-b3ea-6380e0d1edf0) ## inline: * nav to dom element from devtools **disabled** * nav to devtools element from page **disabled** ![after-inline](https://github.com/facebook/react/assets/5188459/fdfdd87b-9bc3-47f3-b1e0-730239f6485d) ## standalone: * nav to dom element from devtools **disabled** * nav to devtools element from page **enabled** and working ![after-standalone](https://github.com/facebook/react/assets/5188459/b25e3c63-a697-4b0c-8ad2-0e12ec5c3e9c) ## fusebox: * nav to dom element from devtools **disabled** * nav to devtools element from page **enabled** and working ![after-fusebox](https://github.com/facebook/react/assets/5188459/f14147d8-9831-4909-a164-52f892c875e5) --- .../react-devtools-core/src/standalone.js | 1 + .../src/main/index.js | 3 ++- .../react-devtools-fusebox/src/frontend.js | 1 + .../src/devtools/store.js | 24 +++++++++++++------ .../views/Components/InspectedElement.js | 2 +- .../src/devtools/views/Components/Tree.js | 2 +- 6 files changed, 23 insertions(+), 10 deletions(-) diff --git a/packages/react-devtools-core/src/standalone.js b/packages/react-devtools-core/src/standalone.js index e4e4ada1c3..541efa0d37 100644 --- a/packages/react-devtools-core/src/standalone.js +++ b/packages/react-devtools-core/src/standalone.js @@ -280,6 +280,7 @@ function initialize(socket: WebSocket) { store = new Store(bridge, { checkBridgeProtocolCompatibility: true, supportsTraceUpdates: true, + supportsClickToInspect: true, }); log('Connected'); diff --git a/packages/react-devtools-extensions/src/main/index.js b/packages/react-devtools-extensions/src/main/index.js index e1db3d5055..5422567db7 100644 --- a/packages/react-devtools-extensions/src/main/index.js +++ b/packages/react-devtools-extensions/src/main/index.js @@ -97,7 +97,8 @@ function createBridgeAndStore() { // At this time, the timeline can only parse Chrome performance profiles. supportsTimeline: __IS_CHROME__, supportsTraceUpdates: true, - supportsNativeInspection: true, + supportsInspectMatchingDOMElement: true, + supportsClickToInspect: true, }); if (!isProfiling) { diff --git a/packages/react-devtools-fusebox/src/frontend.js b/packages/react-devtools-fusebox/src/frontend.js index 976b8693d3..d55241fec7 100644 --- a/packages/react-devtools-fusebox/src/frontend.js +++ b/packages/react-devtools-fusebox/src/frontend.js @@ -37,6 +37,7 @@ export function createStore(bridge: FrontendBridge, config?: Config): Store { return new Store(bridge, { checkBridgeProtocolCompatibility: true, supportsTraceUpdates: true, + supportsClickToInspect: true, ...config, }); } diff --git a/packages/react-devtools-shared/src/devtools/store.js b/packages/react-devtools-shared/src/devtools/store.js index 408151dcdb..ef6f720346 100644 --- a/packages/react-devtools-shared/src/devtools/store.js +++ b/packages/react-devtools-shared/src/devtools/store.js @@ -71,7 +71,8 @@ type ErrorAndWarningTuples = Array<{id: number, index: number}>; export type Config = { checkBridgeProtocolCompatibility?: boolean, isProfiling?: boolean, - supportsNativeInspection?: boolean, + supportsInspectMatchingDOMElement?: boolean, + supportsClickToInspect?: boolean, supportsReloadAndProfile?: boolean, supportsTimeline?: boolean, supportsTraceUpdates?: boolean, @@ -172,7 +173,8 @@ export default class Store extends EventEmitter<{ _rootIDToRendererID: Map = new Map(); // These options may be initially set by a configuration option when constructing the Store. - _supportsNativeInspection: boolean = false; + _supportsInspectMatchingDOMElement: boolean = false; + _supportsClickToInspect: boolean = false; _supportsReloadAndProfile: boolean = false; _supportsTimeline: boolean = false; _supportsTraceUpdates: boolean = false; @@ -211,13 +213,17 @@ export default class Store extends EventEmitter<{ isProfiling = config.isProfiling === true; const { - supportsNativeInspection, + supportsInspectMatchingDOMElement, + supportsClickToInspect, supportsReloadAndProfile, supportsTimeline, supportsTraceUpdates, } = config; - if (supportsNativeInspection) { - this._supportsNativeInspection = true; + if (supportsInspectMatchingDOMElement) { + this._supportsInspectMatchingDOMElement = true; + } + if (supportsClickToInspect) { + this._supportsClickToInspect = true; } if (supportsReloadAndProfile) { this._supportsReloadAndProfile = true; @@ -437,8 +443,12 @@ export default class Store extends EventEmitter<{ return this._rootSupportsTimelineProfiling; } - get supportsNativeInspection(): boolean { - return this._supportsNativeInspection; + get supportsInspectMatchingDOMElement(): boolean { + return this._supportsInspectMatchingDOMElement; + } + + get supportsClickToInspect(): boolean { + return this._supportsClickToInspect; } get supportsNativeStyleEditor(): boolean { diff --git a/packages/react-devtools-shared/src/devtools/views/Components/InspectedElement.js b/packages/react-devtools-shared/src/devtools/views/Components/InspectedElement.js index 8688b132cb..1f1f538b6b 100644 --- a/packages/react-devtools-shared/src/devtools/views/Components/InspectedElement.js +++ b/packages/react-devtools-shared/src/devtools/views/Components/InspectedElement.js @@ -296,7 +296,7 @@ export default function InspectedElementWrapper(_: Props): React.Node { )} - {store.supportsNativeInspection && ( + {store.supportsInspectMatchingDOMElement && (