mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Update base for Update on "[compiler][fixtures] test repros: codegen, alignScope, phis"
The AlignReactiveScope bug should be simplest to fix, but it's also caught by an invariant assertion. I think a fix could be either keeping track of "active" block-fallthrough pairs (`retainWhere(pair => pair.range.end > current.instr[0].id)`) or following the approach in `assertValidBlockNesting`. I'm tempted to pull the value-block aligning logic out into its own pass (using the current `node` tree traversal), then align to non-value blocks with the `assertValidBlockNesting` approach. Happy to hear feedback on this though! The other two are likely bigger issues, as they're not caught by static invariants. Update: - removed bug-phi-reference-effect as it's been patched by josephsavona - added bug-array-concat-should-capture [ghstack-poisoned]
This commit is contained in:
@@ -14,6 +14,12 @@ module.exports = {
|
||||
parser: 'flow',
|
||||
arrowParens: 'avoid',
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.code-workspace'],
|
||||
options: {
|
||||
parser: 'json-stringify',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: esNextPaths,
|
||||
options: {
|
||||
|
||||
@@ -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<t.BinaryExpression["operator"], "|>">;
|
||||
} = {
|
||||
"+=": "+",
|
||||
"-=": "-",
|
||||
"/=": "/",
|
||||
@@ -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",
|
||||
|
||||
@@ -866,7 +866,7 @@ export type InstructionValue =
|
||||
| JSXText
|
||||
| {
|
||||
kind: "BinaryExpression";
|
||||
operator: t.BinaryExpression["operator"];
|
||||
operator: Exclude<t.BinaryExpression["operator"], "|>">;
|
||||
left: Place;
|
||||
right: Place;
|
||||
loc: SourceLocation;
|
||||
@@ -881,7 +881,7 @@ export type InstructionValue =
|
||||
| MethodCall
|
||||
| {
|
||||
kind: "UnaryExpression";
|
||||
operator: t.UnaryExpression["operator"];
|
||||
operator: Exclude<t.UnaryExpression["operator"], "throw" | "delete">;
|
||||
value: Place;
|
||||
loc: SourceLocation;
|
||||
}
|
||||
|
||||
@@ -856,7 +856,7 @@ export function mapTerminalSuccessors(
|
||||
const block = fn(terminal.block);
|
||||
const fallthrough = fn(terminal.fallthrough);
|
||||
return {
|
||||
kind: "scope",
|
||||
kind: terminal.kind,
|
||||
scope: terminal.scope,
|
||||
block,
|
||||
fallthrough,
|
||||
|
||||
+2
-2
@@ -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;
|
||||
}
|
||||
|
||||
+61
@@ -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]
|
||||
+19
@@ -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 }],
|
||||
};
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -280,6 +280,7 @@ function initialize(socket: WebSocket) {
|
||||
store = new Store(bridge, {
|
||||
checkBridgeProtocolCompatibility: true,
|
||||
supportsTraceUpdates: true,
|
||||
supportsClickToInspect: true,
|
||||
});
|
||||
|
||||
log('Connected');
|
||||
|
||||
+2
-1
@@ -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) {
|
||||
|
||||
@@ -37,6 +37,7 @@ export function createStore(bridge: FrontendBridge, config?: Config): Store {
|
||||
return new Store(bridge, {
|
||||
checkBridgeProtocolCompatibility: true,
|
||||
supportsTraceUpdates: true,
|
||||
supportsClickToInspect: true,
|
||||
...config,
|
||||
});
|
||||
}
|
||||
|
||||
+17
-7
@@ -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<number, number> = 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 {
|
||||
|
||||
+1
-1
@@ -296,7 +296,7 @@ export default function InspectedElementWrapper(_: Props): React.Node {
|
||||
<ButtonIcon type="suspend" />
|
||||
</Toggle>
|
||||
)}
|
||||
{store.supportsNativeInspection && (
|
||||
{store.supportsInspectMatchingDOMElement && (
|
||||
<Button
|
||||
onClick={highlightElement}
|
||||
title="Inspect the matching DOM element">
|
||||
|
||||
@@ -361,7 +361,7 @@ export default function Tree(props: Props): React.Node {
|
||||
<TreeFocusedContext.Provider value={treeFocused}>
|
||||
<div className={styles.Tree} ref={treeRef}>
|
||||
<div className={styles.SearchInput}>
|
||||
{store.supportsNativeInspection && (
|
||||
{store.supportsClickToInspect && (
|
||||
<Fragment>
|
||||
<InspectHostNodesToggle />
|
||||
<div className={styles.VRule} />
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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": {
|
||||
|
||||
Vendored
-1
@@ -43,6 +43,5 @@ export {
|
||||
render,
|
||||
unstable_batchedUpdates,
|
||||
findDOMNode,
|
||||
unstable_renderSubtreeIntoContainer,
|
||||
unmountComponentAtNode,
|
||||
} from './client/ReactDOMRootFB';
|
||||
|
||||
@@ -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 <div>{this.context.foo}</div>;
|
||||
}
|
||||
}
|
||||
|
||||
class Parent extends React.Component {
|
||||
static childContextTypes = {
|
||||
foo: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
getChildContext() {
|
||||
return {
|
||||
foo: 'bar',
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return null;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
expect(
|
||||
function () {
|
||||
renderSubtreeIntoContainer(this, <Component />, portal);
|
||||
}.bind(this),
|
||||
).toErrorDev(
|
||||
'ReactDOM.unstable_renderSubtreeIntoContainer() has not been supported since React 18',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const container = document.createElement('div');
|
||||
ReactDOM.render(<Parent />, 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 <div>{this.context.foo + '-' + this.context.getFoo()}</div>;
|
||||
}
|
||||
}
|
||||
|
||||
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, <Component />, portal);
|
||||
}).toErrorDev(
|
||||
'ReactDOM.unstable_renderSubtreeIntoContainer() has not been supported since React 18',
|
||||
);
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
expect(() => {
|
||||
renderSubtreeIntoContainer(this, <Component />, 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(<Parent ref={parentRef} />);
|
||||
});
|
||||
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 <div>{this.context.foo + '-' + this.context.getFoo()}</div>;
|
||||
}
|
||||
}
|
||||
|
||||
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, <Component />, portal);
|
||||
}).toErrorDev(
|
||||
'ReactDOM.unstable_renderSubtreeIntoContainer() has not been supported since React 18',
|
||||
);
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
expect(() => {
|
||||
renderSubtreeIntoContainer(this, <Component />, portal);
|
||||
}).toErrorDev(
|
||||
'ReactDOM.unstable_renderSubtreeIntoContainer() has not been supported since React 18',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const root = ReactDOMClient.createRoot(container);
|
||||
await act(async () => {
|
||||
root.render(<Parent bar="initial" />);
|
||||
});
|
||||
expect(portal.firstChild.innerHTML).toBe('initial-initial');
|
||||
await act(async () => {
|
||||
root.render(<Parent bar="changed" />);
|
||||
});
|
||||
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, <div>hello</div>, portal);
|
||||
}).toErrorDev(
|
||||
'ReactDOM.unstable_renderSubtreeIntoContainer() has not been supported since React 18',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const root = ReactDOMClient.createRoot(container);
|
||||
await act(async () => {
|
||||
root.render(<Parent bar="initial" />);
|
||||
});
|
||||
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 <Middle />;
|
||||
}
|
||||
getChildContext() {
|
||||
return {value: this.props.value};
|
||||
}
|
||||
static childContextTypes = {
|
||||
value: PropTypes.string.isRequired,
|
||||
};
|
||||
}
|
||||
|
||||
class Middle extends React.Component {
|
||||
render() {
|
||||
return null;
|
||||
}
|
||||
componentDidMount() {
|
||||
expect(() => {
|
||||
renderSubtreeIntoContainer(this, <Child />, 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 <div>{this.context.value}</div>;
|
||||
}
|
||||
}
|
||||
|
||||
const root = ReactDOMClient.createRoot(container);
|
||||
await act(async () => {
|
||||
root.render(<Parent value="foo" />);
|
||||
});
|
||||
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, <Middle />, 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, <Child />, 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 <div>{this.context.value}</div>;
|
||||
}
|
||||
}
|
||||
|
||||
const root = ReactDOMClient.createRoot(container);
|
||||
await act(async () => {
|
||||
root.render(<Parent value="foo" />);
|
||||
});
|
||||
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 <div />;
|
||||
}
|
||||
}
|
||||
const c = ReactDOM.render(<C />, 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",
|
||||
);
|
||||
});
|
||||
});
|
||||
-41
@@ -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<any, any>,
|
||||
element: React$Element<any>,
|
||||
containerNode: Container,
|
||||
callback: ?Function,
|
||||
): React$Component<any, any> | 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__) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
+1
-21
@@ -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<string>();
|
||||
didWarnOnInvalidCallback = new Set<string>();
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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+.' +
|
||||
|
||||
@@ -40,7 +40,10 @@ export function useSyncExternalStore<T>(
|
||||
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<T>(
|
||||
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;
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "."
|
||||
}
|
||||
],
|
||||
"extensions": {
|
||||
"recommendations": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"editorconfig.editorconfig",
|
||||
"esbenp.prettier-vscode",
|
||||
"flowtype.flow-for-vscode"
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"search.exclude": {
|
||||
"**/dist/**": true,
|
||||
"**/build/**": true,
|
||||
"**/out/**": true,
|
||||
"*.map": true,
|
||||
"*.log": true
|
||||
},
|
||||
"javascript.validate.enable": false,
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"flow.pathToFlow": "${workspaceFolder}/node_modules/.bin/flow",
|
||||
"prettier.configPath": "",
|
||||
"prettier.ignorePath": ""
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user