@samitouri / QOS-React-2 / commits / a724a3b578

[RFC] Codemod invariant -> throw new Error (#22435)

* Hoist error codes import to module scope When this code was written, the error codes map (`codes.json`) was created on-the-fly, so we had to lazily require from inside the visitor. Because `codes.json` is now checked into source, we can import it a single time in module scope. * Minify error constructors in production We use a script to minify our error messages in production. Each message is assigned an error code, defined in `scripts/error-codes/codes.json`. Then our build script replaces the messages with a link to our error decoder page, e.g. https://reactjs.org/docs/error-decoder.html/?invariant=92 This enables us to write helpful error messages without increasing the bundle size. Right now, the script only works for `invariant` calls. It does not work if you throw an Error object. This is an old Facebookism that we don't really need, other than the fact that our error minification script relies on it. So, I've updated the script to minify error constructors, too: Input: Error(`A ${adj} message that contains ${noun}`); Output: Error(formatProdErrorMessage(ERR_CODE, adj, noun)); It only works for constructors that are literally named Error, though we could add support for other names, too. As a next step, I will add a lint rule to enforce that errors written this way must have a corresponding error code. * Minify "no fallback UI specified" error in prod This error message wasn't being minified because it doesn't use invariant. The reason it didn't use invariant is because this particular error is created without begin thrown — it doesn't need to be thrown because it's located inside the error handling part of the runtime. Now that the error minification script supports Error constructors, we can minify it by assigning it a production error code in `scripts/error-codes/codes.json`. To support the use of Error constructors more generally, I will add a lint rule that enforces each message has a corresponding error code. * Lint rule to detect unminified errors Adds a lint rule that detects when an Error constructor is used without a corresponding production error code. We already have this for `invariant`, but not for regular errors, i.e. `throw new Error(msg)`. There's also nothing that enforces the use of `invariant` besides convention. There are some packages where we don't care to minify errors. These are packages that run in environments where bundle size is not a concern, like react-pg. I added an override in the ESLint config to ignore these. * Temporarily add invariant codemod script I'm adding this codemod to the repo temporarily, but I'll revert it in the same PR. That way we don't have to check it in but it's still accessible (via the PR) if we need it later. * [Automated] Codemod invariant -> Error This commit contains only automated changes: npx jscodeshift -t scripts/codemod-invariant.js packages --ignore-pattern="node_modules/**/*" yarn linc --fix yarn prettier I will do any manual touch ups in separate commits so they're easier to review. * Remove temporary codemod script This reverts the codemod script and ESLint config I added temporarily in order to perform the invariant codemod. * Manual touch ups A few manual changes I made after the codemod ran. * Enable error code transform per package Currently we're not consistent about which packages should have their errors minified in production and which ones should. This adds a field to the bundle configuration to control whether to apply the transform. We should decide what the criteria is going forward. I think it's probably a good idea to minify any package that gets sent over the network. So yes to modules that run in the browser, and no to modules that run on the server and during development only.

Andrew Clark committed Sep 30, 2021 at 15:01 UTC a724a3b578dce77d427bef313102a4d0e978d9b4
128 files changed +2118 -1519
.eslintrc.js
+35
@@ -127,6 +127,41 @@ module.exports = {
127 },
128
129 overrides: [
130 + {
131 + // By default, anything error message that appears the packages directory
132 + // must have a corresponding error code. The exceptions are defined
133 + // in the next override entry.
134 + files: ['packages/**/*.js'],
135 + rules: {
136 + 'react-internal/prod-error-codes': ERROR,
137 + },
138 + },
139 + {
140 + // These are files where it's OK to have unminified error messages. These
141 + // are environments where bundle size isn't a concern, like tests
142 + // or Node.
143 + files: [
144 + 'packages/react-dom/src/test-utils/**/*.js',
145 + 'packages/react-devtools-shared/**/*.js',
146 + 'packages/react-noop-renderer/**/*.js',
147 + 'packages/react-pg/**/*.js',
148 + 'packages/react-fs/**/*.js',
149 + 'packages/react-refresh/**/*.js',
150 + 'packages/react-server-dom-webpack/**/*.js',
151 + 'packages/react-test-renderer/**/*.js',
152 + 'packages/react-debug-tools/**/*.js',
153 + 'packages/react-devtools-extensions/**/*.js',
154 + 'packages/react-devtools-scheduling-profiler/**/*.js',
155 + 'packages/react-native-renderer/**/*.js',
156 + 'packages/eslint-plugin-react-hooks/**/*.js',
157 + 'packages/jest-react/**/*.js',
158 + 'packages/**/__tests__/*.js',
159 + 'packages/**/npm/*.js',
160 + ],
161 + rules: {
162 + 'react-internal/prod-error-codes': OFF,
163 + },
164 + },
165 {
166 // We apply these settings to files that we ship through npm.
167 // They must be ES5.
packages/create-subscription/src/createSubscription.js
+6 -5
@@ -8,7 +8,6 @@
8 */
9
10 import * as React from 'react';
11 -import invariant from 'shared/invariant';
11
12 type Unsubscribe = () => void;
13
@@ -128,10 +127,12 @@ export function createSubscription<Property, Value>(
127
128 // Store the unsubscribe method for later (in case the subscribable prop changes).
129 const unsubscribe = subscribe(source, callback);
131 - invariant(
132 - typeof unsubscribe === 'function',
133 - 'A subscription must return an unsubscribe function.',
134 - );
130 +
131 + if (typeof unsubscribe !== 'function') {
132 + throw new Error(
133 + 'A subscription must return an unsubscribe function.',
134 + );
135 + }
136
137 // It's safe to store unsubscribe on the instance because
138 // We only read or write that property during the "commit" phase.
packages/jest-react/src/JestReact.js
+6 -6
@@ -7,7 +7,6 @@
7
8 import {REACT_ELEMENT_TYPE, REACT_FRAGMENT_TYPE} from 'shared/ReactSymbols';
9
10 -import invariant from 'shared/invariant';
10 import isArray from 'shared/isArray';
11
12 export {act} from './internalAct';
@@ -31,11 +30,12 @@ function captureAssertion(fn) {
30 function assertYieldsWereCleared(root) {
31 const Scheduler = root._Scheduler;
32 const actualYields = Scheduler.unstable_clearYields();
34 - invariant(
35 - actualYields.length === 0,
36 - 'Log of yielded values is not empty. ' +
37 - 'Call expect(ReactTestRenderer).unstable_toHaveYielded(...) first.',
38 - );
33 + if (actualYields.length !== 0) {
34 + throw new Error(
35 + 'Log of yielded values is not empty. ' +
36 + 'Call expect(ReactTestRenderer).unstable_toHaveYielded(...) first.',
37 + );
38 + }
39 }
40
41 export function unstable_toMatchRenderedOutput(root, expectedJSX) {
packages/react-art/src/ReactARTHostConfig.js
+17 -17
@@ -7,7 +7,6 @@
7
8 import Transform from 'art/core/transform';
9 import Mode from 'art/modes/current';
10 -import invariant from 'shared/invariant';
10
11 import {TYPES, EVENT_TYPES, childrenAsString} from './ReactARTInternals';
12
@@ -248,8 +247,7 @@ export * from 'react-reconciler/src/ReactFiberHostConfigWithNoMicrotasks';
247 export function appendInitialChild(parentInstance, child) {
248 if (typeof child === 'string') {
249 // Noop for string children of Text (eg <Text>{'foo'}{'bar'}</Text>)
251 - invariant(false, 'Text children should already be flattened.');
252 - return;
250 + throw new Error('Text children should already be flattened.');
251 }
252
253 child.inject(parentInstance);
@@ -282,7 +280,9 @@ export function createInstance(type, props, internalInstanceHandle) {
280 break;
281 }
282
285 - invariant(instance, 'ReactART does not support the type "%s"', type);
283 + if (!instance) {
284 + throw new Error(`ReactART does not support the type "${type}"`);
285 + }
286
287 instance._applyProps(instance, props);
288
@@ -367,18 +367,18 @@ export function appendChildToContainer(parentInstance, child) {
367 }
368
369 export function insertBefore(parentInstance, child, beforeChild) {
370 - invariant(
371 - child !== beforeChild,
372 - 'ReactART: Can not insert node before itself',
373 - );
370 + if (child === beforeChild) {
371 + throw new Error('ReactART: Can not insert node before itself');
372 + }
373 +
374 child.injectBefore(beforeChild);
375 }
376
377 export function insertInContainerBefore(parentInstance, child, beforeChild) {
378 - invariant(
379 - child !== beforeChild,
380 - 'ReactART: Can not insert node before itself',
381 - );
378 + if (child === beforeChild) {
379 + throw new Error('ReactART: Can not insert node before itself');
380 + }
381 +
382 child.injectBefore(beforeChild);
383 }
384
@@ -433,25 +433,25 @@ export function clearContainer(container) {
433 }
434
435 export function getInstanceFromNode(node) {
436 - throw new Error('Not yet implemented.');
436 + throw new Error('Not implemented.');
437 }
438
439 export function isOpaqueHydratingObject(value: mixed): boolean {
440 - throw new Error('Not yet implemented');
440 + throw new Error('Not implemented.');
441 }
442
443 export function makeOpaqueHydratingObject(
444 attemptToReadValue: () => void,
445 ): OpaqueIDType {
446 - throw new Error('Not yet implemented.');
446 + throw new Error('Not implemented.');
447 }
448
449 export function makeClientId(): OpaqueIDType {
450 - throw new Error('Not yet implemented');
450 + throw new Error('Not implemented.');
451 }
452
453 export function makeClientIdInDEV(warnOnAccessInDEV: () => void): OpaqueIDType {
454 - throw new Error('Not yet implemented');
454 + throw new Error('Not implemented.');
455 }
456
457 export function beforeActiveInstanceBlur(internalInstanceHandle: Object) {
packages/react-cache/src/ReactCacheOld.js
+2
@@ -49,6 +49,8 @@ const ReactCurrentDispatcher =
49 function readContext(Context) {
50 const dispatcher = ReactCurrentDispatcher.current;
51 if (dispatcher === null) {
52 + // This wasn't being minified but we're going to retire this package anyway.
53 + // eslint-disable-next-line react-internal/prod-error-codes
54 throw new Error(
55 'react-cache: read and preload may only be called from within a ' +
56 "component's render. They are not supported in event handlers or " +
packages/react-client/src/ReactFlightClient.js
+1
@@ -397,6 +397,7 @@ export function resolveError(
397 message: string,
398 stack: string,
399 ): void {
400 + // eslint-disable-next-line react-internal/prod-error-codes
401 const error = new Error(message);
402 error.stack = stack;
403 const chunks = response._chunks;
packages/react-client/src/ReactFlightClientHostConfig.js
+2 -4
@@ -7,9 +7,7 @@
7 * @flow
8 */
9
10 -/* eslint-disable react-internal/invariant-args */
11 -
12 -import invariant from 'shared/invariant';
10 +/* eslint-disable react-internal/prod-error-codes */
11
12 // We expect that our Rollup, Jest, and Flow configurations
13 // always shim this module with the corresponding host config
@@ -19,4 +17,4 @@ import invariant from 'shared/invariant';
17 // sure that if we *do* accidentally break the configuration,
18 // the failure isn't silent.
19
22 -invariant(false, 'This module must be shimmed by a specific renderer.');
20 +throw new Error('This module must be shimmed by a specific renderer.');
packages/react-client/src/ReactFlightClientHostConfigNoStream.js
+3
@@ -12,6 +12,7 @@ export type StringDecoder = void;
12 export const supportsBinaryStreams = false;
13
14 export function createStringDecoder(): void {
15 + // eslint-disable-next-line react-internal/prod-error-codes
16 throw new Error('Should never be called');
17 }
18
@@ -19,6 +20,7 @@ export function readPartialStringChunk(
20 decoder: StringDecoder,
21 buffer: Uint8Array,
22 ): string {
23 + // eslint-disable-next-line react-internal/prod-error-codes
24 throw new Error('Should never be called');
25 }
26
@@ -26,5 +28,6 @@ export function readFinalStringChunk(
28 decoder: StringDecoder,
29 buffer: Uint8Array,
30 ): string {
31 + // eslint-disable-next-line react-internal/prod-error-codes
32 throw new Error('Should never be called');
33 }
packages/react-debug-tools/src/ReactDebugHooks.js
+1 -2
@@ -23,7 +23,6 @@ import type {OpaqueIDType} from 'react-reconciler/src/ReactFiberHostConfig';
23 import {NoMode} from 'react-reconciler/src/ReactTypeOfMode';
24
25 import ErrorStackParser from 'error-stack-parser';
26 -import invariant from 'shared/invariant';
26 import ReactSharedInternals from 'shared/ReactSharedInternals';
27 import {REACT_OPAQUE_ID_TYPE} from 'shared/ReactSymbols';
28 import {
@@ -107,7 +106,7 @@ function nextHook(): null | Hook {
106 }
107
108 function getCacheForType<T>(resourceType: () => T): T {
110 - invariant(false, 'Not implemented.');
109 + throw new Error('Not implemented.');
110 }
111
112 function readContext<T>(context: ReactContext<T>): T {
packages/react-dom/src/client/ReactDOM.js
+4 -5
@@ -39,7 +39,6 @@ import {
39 import {createPortal as createPortalImpl} from 'react-reconciler/src/ReactPortal';
40 import {canUseDOM} from 'shared/ExecutionEnvironment';
41 import ReactVersion from 'shared/ReactVersion';
42 -import invariant from 'shared/invariant';
42 import {
43 warnUnstableRenderSubtreeIntoContainer,
44 enableNewReconciler,
@@ -108,10 +107,10 @@ function createPortal(
107 container: Container,
108 key: ?string = null,
109 ): React$Portal {
111 - invariant(
112 - isValidContainer(container),
113 - 'Target container is not a DOM element.',
114 - );
110 + if (!isValidContainer(container)) {
111 + throw new Error('Target container is not a DOM element.');
112 + }
113 +
114 // TODO: pass ReactDOM portal implementation as third argument
115 // $FlowFixMe The Flow type is opaque but there's no way to actually create it.
116 return createPortalImpl(children, container, null, key);
packages/react-dom/src/client/ReactDOMComponentTree.js
+1 -2
@@ -30,7 +30,6 @@ import {
30
31 import {getParentSuspenseInstance} from './ReactDOMHostConfig';
32
33 -import invariant from 'shared/invariant';
33 import {enableScopeAPI} from 'shared/ReactFeatureFlags';
34
35 const randomKey = Math.random()
@@ -190,7 +189,7 @@ export function getNodeFromInstance(inst: Fiber): Instance | TextInstance {
189
190 // Without this first invariant, passing a non-DOM-component triggers the next
191 // invariant for a missing parent, which is super confusing.
193 - invariant(false, 'getNodeFromInstance: Invalid argument.');
192 + throw new Error('getNodeFromInstance: Invalid argument.');
193 }
194
195 export function getFiberCurrentPropsFromNode(
packages/react-dom/src/client/ReactDOMEventHandle.js
+13 -13
@@ -28,7 +28,6 @@ import {
28 enableScopeAPI,
29 enableCreateEventHandleAPI,
30 } from 'shared/ReactFeatureFlags';
31 -import invariant from 'shared/invariant';
31
32 type EventHandleOptions = {|
33 capture?: boolean,
@@ -73,8 +72,7 @@ function registerReactDOMEvent(
72 eventTarget,
73 );
74 } else {
76 - invariant(
77 - false,
75 + throw new Error(
76 'ReactDOM.createEventHandle: setter called on an invalid ' +
77 'target. Provide a valid EventTarget or an element managed by React.',
78 );
@@ -97,11 +95,11 @@ export function createEventHandle(
95 // Unfortunately, the downside of this invariant is that *removing* a native
96 // event from the list of known events has now become a breaking change for
97 // any code relying on the createEventHandle API.
100 - invariant(
101 - allNativeEvents.has(domEventName),
102 - 'Cannot call unstable_createEventHandle with "%s", as it is not an event known to React.',
103 - domEventName,
104 - );
98 + if (!allNativeEvents.has(domEventName)) {
99 + throw new Error(
100 + `Cannot call unstable_createEventHandle with "${domEventName}", as it is not an event known to React.`,
101 + );
102 + }
103
104 let isCapturePhaseListener = false;
105 if (options != null) {
@@ -115,11 +113,13 @@ export function createEventHandle(
113 target: EventTarget | ReactScopeInstance,
114 callback: (SyntheticEvent<EventTarget>) => void,
115 ) => {
118 - invariant(
119 - typeof callback === 'function',
120 - 'ReactDOM.createEventHandle: setter called with an invalid ' +
121 - 'callback. The callback must be a function.',
122 - );
116 + if (typeof callback !== 'function') {
117 + throw new Error(
118 + 'ReactDOM.createEventHandle: setter called with an invalid ' +
119 + 'callback. The callback must be a function.',
120 + );
121 + }
122 +
123 if (!doesTargetHaveEventHandle(target, eventHandle)) {
124 addEventHandleToTarget(target, eventHandle);
125 registerReactDOMEvent(target, domEventName, isCapturePhaseListener);
packages/react-dom/src/client/ReactDOMInput.js
+7 -6
@@ -9,7 +9,6 @@
9
10 // TODO: direct imports like some-package/src/* are bad. Fix me.
11 import {getCurrentFiberOwnerNameInDevOrNull} from 'react-reconciler/src/ReactCurrentFiber';
12 -import invariant from 'shared/invariant';
12
13 import {setValueForProperty} from './DOMPropertyOperations';
14 import {getFiberCurrentPropsFromNode} from './ReactDOMComponentTree';
@@ -383,11 +382,13 @@ function updateNamedCousins(rootNode, props) {
382 // That's probably okay; we don't support it just as we don't support
383 // mixing React radio buttons with non-React ones.
384 const otherProps = getFiberCurrentPropsFromNode(otherNode);
386 - invariant(
387 - otherProps,
388 - 'ReactDOMInput: Mixing React and non-React radio inputs with the ' +
389 - 'same `name` is not supported.',
390 - );
385 +
386 + if (!otherProps) {
387 + throw new Error(
388 + 'ReactDOMInput: Mixing React and non-React radio inputs with the ' +
389 + 'same `name` is not supported.',
390 + );
391 + }
392
393 // We need update the tracked value on the named cousin since the value
394 // was changed but the input saw no event or value set
packages/react-dom/src/client/ReactDOMLegacy.js
+21 -21
@@ -36,7 +36,6 @@ import {
36 } from 'react-reconciler/src/ReactFiberReconciler';
37 import {LegacyRoot} from 'react-reconciler/src/ReactRootTags';
38 import getComponentNameFromType from 'shared/getComponentNameFromType';
39 -import invariant from 'shared/invariant';
39 import ReactSharedInternals from 'shared/ReactSharedInternals';
40 import {has as hasInstance} from 'shared/ReactInstanceMap';
41
@@ -238,10 +237,10 @@ export function hydrate(
237 );
238 }
239
241 - invariant(
242 - isValidContainerLegacy(container),
243 - 'Target container is not a DOM element.',
244 - );
240 + if (!isValidContainerLegacy(container)) {
241 + throw new Error('Target container is not a DOM element.');
242 + }
243 +
244 if (__DEV__) {
245 const isModernRoot =
246 isContainerMarkedAsRoot(container) &&
@@ -278,10 +277,10 @@ export function render(
277 );
278 }
279
281 - invariant(
282 - isValidContainerLegacy(container),
283 - 'Target container is not a DOM element.',
284 - );
280 + if (!isValidContainerLegacy(container)) {
281 + throw new Error('Target container is not a DOM element.');
282 + }
283 +
284 if (__DEV__) {
285 const isModernRoot =
286 isContainerMarkedAsRoot(container) &&
@@ -309,14 +308,14 @@ export function unstable_renderSubtreeIntoContainer(
308 containerNode: Container,
309 callback: ?Function,
310 ) {
312 - invariant(
313 - isValidContainerLegacy(containerNode),
314 - 'Target container is not a DOM element.',
315 - );
316 - invariant(
317 - parentComponent != null && hasInstance(parentComponent),
318 - 'parentComponent must be a valid React Component',
319 - );
311 + if (!isValidContainerLegacy(containerNode)) {
312 + throw new Error('Target container is not a DOM element.');
313 + }
314 +
315 + if (parentComponent == null || !hasInstance(parentComponent)) {
316 + throw new Error('parentComponent must be a valid React Component');
317 + }
318 +
319 return legacyRenderSubtreeIntoContainer(
320 parentComponent,
321 element,
@@ -327,10 +326,11 @@ export function unstable_renderSubtreeIntoContainer(
326 }
327
328 export function unmountComponentAtNode(container: Container) {
330 - invariant(
331 - isValidContainerLegacy(container),
332 - 'unmountComponentAtNode(...): Target container is not a DOM element.',
333 - );
329 + if (!isValidContainerLegacy(container)) {
330 + throw new Error(
331 + 'unmountComponentAtNode(...): Target container is not a DOM element.',
332 + );
333 + }
334
335 if (__DEV__) {
336 const isModernRoot =
packages/react-dom/src/client/ReactDOMRoot.js
+9 -10
@@ -65,7 +65,6 @@ import {
65 flushSync,
66 isAlreadyRendering,
67 } from 'react-reconciler/src/ReactFiberReconciler';
68 -import invariant from 'shared/invariant';
68 import {ConcurrentRoot} from 'react-reconciler/src/ReactRootTags';
69 import {allowConcurrentByDefault} from 'shared/ReactFeatureFlags';
70
@@ -76,7 +75,7 @@ function ReactDOMRoot(internalRoot: FiberRoot) {
75 ReactDOMRoot.prototype.render = function(children: ReactNodeList): void {
76 const root = this._internalRoot;
77 if (root === null) {
79 - invariant(false, 'Cannot update an unmounted root.');
78 + throw new Error('Cannot update an unmounted root.');
79 }
80
81 if (__DEV__) {
@@ -138,10 +137,10 @@ export function createRoot(
137 container: Container,
138 options?: CreateRootOptions,
139 ): RootType {
141 - invariant(
142 - isValidContainerLegacy(container),
143 - 'createRoot(...): Target container is not a DOM element.',
144 - );
140 + if (!isValidContainerLegacy(container)) {
141 + throw new Error('createRoot(...): Target container is not a DOM element.');
142 + }
143 +
144 warnIfReactDOMContainerInDEV(container);
145
146 // TODO: Delete these options
@@ -195,10 +194,10 @@ export function hydrateRoot(
194 initialChildren: ReactNodeList,
195 options?: HydrateRootOptions,
196 ): RootType {
198 - invariant(
199 - isValidContainer(container),
200 - 'hydrateRoot(...): Target container is not a DOM element.',
201 - );
197 + if (!isValidContainer(container)) {
198 + throw new Error('hydrateRoot(...): Target container is not a DOM element.');
199 + }
200 +
201 warnIfReactDOMContainerInDEV(container);
202
203 // For now we reuse the whole bag of options since they contain
packages/react-dom/src/client/ReactDOMTextarea.js
+16 -13
@@ -7,7 +7,6 @@
7 * @flow
8 */
9
10 -import invariant from 'shared/invariant';
10 import isArray from 'shared/isArray';
11
12 import {checkControlledValueProps} from '../shared/ReactControlledValuePropTypes';
@@ -40,10 +39,12 @@ type TextAreaWithWrapperState = HTMLTextAreaElement & {|
39
40 export function getHostProps(element: Element, props: Object) {
41 const node = ((element: any): TextAreaWithWrapperState);
43 - invariant(
44 - props.dangerouslySetInnerHTML == null,
45 - '`dangerouslySetInnerHTML` does not make sense on <textarea>.',
46 - );
42 +
43 + if (props.dangerouslySetInnerHTML != null) {
44 + throw new Error(
45 + '`dangerouslySetInnerHTML` does not make sense on <textarea>.',
46 + );
47 + }
48
49 // Always set children to the same thing. In IE9, the selection range will
50 // get reset if `textContent` is mutated. We could add a check in setTextContent
@@ -96,15 +97,17 @@ export function initWrapperState(element: Element, props: Object) {
97 );
98 }
99 if (!disableTextareaChildren) {
99 - invariant(
100 - defaultValue == null,
101 - 'If you supply `defaultValue` on a <textarea>, do not pass children.',
102 - );
103 - if (isArray(children)) {
104 - invariant(
105 - children.length <= 1,
106 - '<textarea> can only have at most one child.',
100 + if (defaultValue != null) {
101 + throw new Error(
102 + 'If you supply `defaultValue` on a <textarea>, do not pass children.',
103 );
104 + }
105 +
106 + if (isArray(children)) {
107 + if (children.length > 1) {
108 + throw new Error('<textarea> can only have at most one child.');
109 + }
110 +
111 children = children[0];
112 }
113
packages/react-dom/src/events/ReactDOMControlledComponent.js
+8 -6
@@ -7,7 +7,6 @@
7 * @flow
8 */
9
10 -import invariant from 'shared/invariant';
10 import {
11 getInstanceFromNode,
12 getFiberCurrentPropsFromNode,
@@ -27,11 +26,14 @@ function restoreStateOfTarget(target: Node) {
26 // Unmounted
27 return;
28 }
30 - invariant(
31 - typeof restoreImpl === 'function',
32 - 'setRestoreImplementation() needs to be called to handle a target for controlled ' +
33 - 'events. This error is likely caused by a bug in React. Please file an issue.',
34 - );
29 +
30 + if (typeof restoreImpl !== 'function') {
31 + throw new Error(
32 + 'setRestoreImplementation() needs to be called to handle a target for controlled ' +
33 + 'events. This error is likely caused by a bug in React. Please file an issue.',
34 + );
35 + }
36 +
37 const stateNode = internalInstance.stateNode;
38 // Guard against Fiber being unmounted.
39 if (stateNode) {
packages/react-dom/src/events/getListener.js
+7 -7
@@ -9,7 +9,6 @@
9 import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
10 import type {Props} from '../client/ReactDOMHostConfig';
11
12 -import invariant from 'shared/invariant';
12 import {getFiberCurrentPropsFromNode} from '../client/ReactDOMComponentTree';
13
14 function isInteractive(tag: string): boolean {
@@ -67,11 +66,12 @@ export default function getListener(
66 if (shouldPreventMouseEvent(registrationName, inst.type, props)) {
67 return null;
68 }
70 - invariant(
71 - !listener || typeof listener === 'function',
72 - 'Expected `%s` listener to be a function, instead got a value of `%s` type.',
73 - registrationName,
74 - typeof listener,
75 - );
69 +
70 + if (listener && typeof listener !== 'function') {
71 + throw new Error(
72 + `Expected \`${registrationName}\` listener to be a function, instead got a value of \`${typeof listener}\` type.`,
73 + );
74 + }
75 +
76 return listener;
77 }
packages/react-dom/src/server/ReactDOMLegacyServerBrowser.js
+12 -12
@@ -8,7 +8,6 @@
8 */
9
10 import ReactVersion from 'shared/ReactVersion';
11 -import invariant from 'shared/invariant';
11
12 import type {ReactNodeList} from 'shared/ReactTypes';
13
@@ -77,13 +76,16 @@ function renderToStringImpl(
76 if (didFatal) {
77 throw fatalError;
78 }
80 - invariant(
81 - readyToStream,
82 - 'A React component suspended while rendering, but no fallback UI was specified.\n' +
83 - '\n' +
84 - 'Add a <Suspense fallback=...> component higher in the tree to ' +
85 - 'provide a loading indicator or placeholder to display.',
86 - );
79 +
80 + if (!readyToStream) {
81 + throw new Error(
82 + 'A React component suspended while rendering, but no fallback UI was specified.\n' +
83 + '\n' +
84 + 'Add a <Suspense fallback=...> component higher in the tree to ' +
85 + 'provide a loading indicator or placeholder to display.',
86 + );
87 + }
88 +
89 return result;
90 }
91
@@ -102,16 +104,14 @@ function renderToStaticMarkup(
104 }
105
106 function renderToNodeStream() {
105 - invariant(
106 - false,
107 + throw new Error(
108 'ReactDOMServer.renderToNodeStream(): The streaming API is not available ' +
109 'in the browser. Use ReactDOMServer.renderToString() instead.',
110 );
111 }
112
113 function renderToStaticNodeStream() {
113 - invariant(
114 - false,
114 + throw new Error(
115 'ReactDOMServer.renderToStaticNodeStream(): The streaming API is not available ' +
116 'in the browser. Use ReactDOMServer.renderToStaticMarkup() instead.',
117 );
packages/react-dom/src/server/ReactDOMServerFormatConfig.js
+83 -66
@@ -49,7 +49,6 @@ import warnValidStyle from '../shared/warnValidStyle';
49
50 import escapeTextForBrowser from './escapeTextForBrowser';
51 import hyphenateStyleName from '../shared/hyphenateStyleName';
52 -import invariant from 'shared/invariant';
52 import hasOwnProperty from 'shared/hasOwnProperty';
53 import sanitizeURL from '../shared/sanitizeURL';
54 import isArray from 'shared/isArray';
@@ -192,10 +191,12 @@ export type OpaqueIDType = string;
191 export function makeServerID(
192 responseState: null | ResponseState,
193 ): OpaqueIDType {
195 - invariant(
196 - responseState !== null,
197 - 'Invalid hook call. Hooks can only be called inside of the body of a function component.',
198 - );
194 + if (responseState === null) {
195 + throw new Error(
196 + 'Invalid hook call. Hooks can only be called inside of the body of a function component.',
197 + );
198 + }
199 +
200 // TODO: This is not deterministic since it's created during render.
201 return (
202 responseState.opaqueIdentifierPrefix +
@@ -244,12 +245,13 @@ function pushStyle(
245 responseState: ResponseState,
246 style: Object,
247 ): void {
247 - invariant(
248 - typeof style === 'object',
249 - 'The `style` prop expects a mapping from style properties to values, ' +
250 - "not a string. For example, style={{marginRight: spacing + 'em'}} when " +
251 - 'using JSX.',
252 - );
248 + if (typeof style !== 'object') {
249 + throw new Error(
250 + 'The `style` prop expects a mapping from style properties to values, ' +
251 + "not a string. For example, style={{marginRight: spacing + 'em'}} when " +
252 + 'using JSX.',
253 + );
254 + }
255
256 let isFirst = true;
257 for (const styleName in style) {
@@ -498,17 +500,20 @@ function pushInnerHTML(
500 children,
501 ) {
502 if (innerHTML != null) {
501 - invariant(
502 - children == null,
503 - 'Can only set one of `children` or `props.dangerouslySetInnerHTML`.',
504 - );
503 + if (children != null) {
504 + throw new Error(
505 + 'Can only set one of `children` or `props.dangerouslySetInnerHTML`.',
506 + );
507 + }
508 +
509 + if (typeof innerHTML !== 'object' || !('__html' in innerHTML)) {
510 + throw new Error(
511 + '`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. ' +
512 + 'Please visit https://reactjs.org/link/dangerously-set-inner-html ' +
513 + 'for more information.',
514 + );
515 + }
516
506 - invariant(
507 - typeof innerHTML === 'object' && '__html' in innerHTML,
508 - '`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. ' +
509 - 'Please visit https://reactjs.org/link/dangerously-set-inner-html ' +
510 - 'for more information.',
511 - );
517 const html = innerHTML.__html;
518 if (html !== null && html !== undefined) {
519 if (__DEV__) {
@@ -799,11 +804,9 @@ function pushInput(
804 switch (propKey) {
805 case 'children':
806 case 'dangerouslySetInnerHTML':
802 - invariant(
803 - false,
804 - '%s is a self-closing tag and must neither have `children` nor ' +
807 + throw new Error(
808 + `${'input'} is a self-closing tag and must neither have \`children\` nor ` +
809 'use `dangerouslySetInnerHTML`.',
806 - 'input',
810 );
811 // eslint-disable-next-line-no-fallthrough
812 case 'defaultChecked':
@@ -885,8 +888,7 @@ function pushStartTextArea(
888 defaultValue = propValue;
889 break;
890 case 'dangerouslySetInnerHTML':
888 - invariant(
889 - false,
891 + throw new Error(
892 '`dangerouslySetInnerHTML` does not make sense on <textarea>.',
893 );
894 // eslint-disable-next-line-no-fallthrough
@@ -910,15 +912,18 @@ function pushStartTextArea(
912 'children on <textarea>.',
913 );
914 }
913 - invariant(
914 - value == null,
915 - 'If you supply `defaultValue` on a <textarea>, do not pass children.',
916 - );
917 - if (isArray(children)) {
918 - invariant(
919 - children.length <= 1,
920 - '<textarea> can only have at most one child.',
915 +
916 + if (value != null) {
917 + throw new Error(
918 + 'If you supply `defaultValue` on a <textarea>, do not pass children.',
919 );
920 + }
921 +
922 + if (isArray(children)) {
923 + if (children.length > 1) {
924 + throw new Error('<textarea> can only have at most one child.');
925 + }
926 +
927 // TODO: remove the coercion and the DEV check below because it will
928 // always be overwritten by the coercion several lines below it. #22309
929 if (__DEV__) {
@@ -966,11 +971,9 @@ function pushSelfClosing(
971 switch (propKey) {
972 case 'children':
973 case 'dangerouslySetInnerHTML':
969 - invariant(
970 - false,
971 - '%s is a self-closing tag and must neither have `children` nor ' +
974 + throw new Error(
975 + `${tag} is a self-closing tag and must neither have \`children\` nor ` +
976 'use `dangerouslySetInnerHTML`.',
973 - tag,
977 );
978 // eslint-disable-next-line-no-fallthrough
979 default:
@@ -1000,8 +1003,7 @@ function pushStartMenuItem(
1003 switch (propKey) {
1004 case 'children':
1005 case 'dangerouslySetInnerHTML':
1003 - invariant(
1004 - false,
1006 + throw new Error(
1007 'menuitems cannot have `children` nor `dangerouslySetInnerHTML`.',
1008 );
1009 // eslint-disable-next-line-no-fallthrough
@@ -1158,17 +1160,20 @@ function pushStartPreformattedElement(
1160 // TODO: This doesn't deal with the case where the child is an array
1161 // or component that returns a string.
1162 if (innerHTML != null) {
1161 - invariant(
1162 - children == null,
1163 - 'Can only set one of `children` or `props.dangerouslySetInnerHTML`.',
1164 - );
1163 + if (children != null) {
1164 + throw new Error(
1165 + 'Can only set one of `children` or `props.dangerouslySetInnerHTML`.',
1166 + );
1167 + }
1168 +
1169 + if (typeof innerHTML !== 'object' || !('__html' in innerHTML)) {
1170 + throw new Error(
1171 + '`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. ' +
1172 + 'Please visit https://reactjs.org/link/dangerously-set-inner-html ' +
1173 + 'for more information.',
1174 + );
1175 + }
1176
1166 - invariant(
1167 - typeof innerHTML === 'object' && '__html' in innerHTML,
1168 - '`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. ' +
1169 - 'Please visit https://reactjs.org/link/dangerously-set-inner-html ' +
1170 - 'for more information.',
1171 - );
1177 const html = innerHTML.__html;
1178 if (html !== null && html !== undefined) {
1179 if (typeof html === 'string' && html.length > 0 && html[0] === '\n') {
@@ -1195,7 +1200,10 @@ const validatedTagCache = new Map();
1200 function startChunkForTag(tag: string): PrecomputedChunk {
1201 let tagStartChunk = validatedTagCache.get(tag);
1202 if (tagStartChunk === undefined) {
1198 - invariant(VALID_TAG_REGEX.test(tag), 'Invalid tag: %s', tag);
1203 + if (!VALID_TAG_REGEX.test(tag)) {
1204 + throw new Error(`Invalid tag: ${tag}`);
1205 + }
1206 +
1207 tagStartChunk = stringToPrecomputedChunk('<' + tag);
1208 validatedTagCache.set(tag, tagStartChunk);
1209 }
@@ -1405,10 +1413,13 @@ export function writeStartPendingSuspenseBoundary(
1413 id: SuspenseBoundaryID,
1414 ): boolean {
1415 writeChunk(destination, startPendingSuspenseBoundary1);
1408 - invariant(
1409 - id !== null,
1410 - 'An ID must have been assigned before we can complete the boundary.',
1411 - );
1416 +
1417 + if (id === null) {
1418 + throw new Error(
1419 + 'An ID must have been assigned before we can complete the boundary.',
1420 + );
1421 + }
1422 +
1423 writeChunk(destination, id);
1424 return writeChunk(destination, startPendingSuspenseBoundary2);
1425 }
@@ -1528,7 +1539,7 @@ export function writeStartSegment(
1539 return writeChunk(destination, startSegmentColGroup2);
1540 }
1541 default: {
1531 - invariant(false, 'Unknown insertion mode. This is a bug in React.');
1542 + throw new Error('Unknown insertion mode. This is a bug in React.');
1543 }
1544 }
1545 }
@@ -1560,7 +1571,7 @@ export function writeEndSegment(
1571 return writeChunk(destination, endSegmentColGroup);
1572 }
1573 default: {
1563 - invariant(false, 'Unknown insertion mode. This is a bug in React.');
1574 + throw new Error('Unknown insertion mode. This is a bug in React.');
1575 }
1576 }
1577 }
@@ -1729,10 +1740,13 @@ export function writeCompletedBoundaryInstruction(
1740 // Future calls can just reuse the same function.
1741 writeChunk(destination, completeBoundaryScript1Partial);
1742 }
1732 - invariant(
1733 - boundaryID !== null,
1734 - 'An ID must have been assigned before we can complete the boundary.',
1735 - );
1743 +
1744 + if (boundaryID === null) {
1745 + throw new Error(
1746 + 'An ID must have been assigned before we can complete the boundary.',
1747 + );
1748 + }
1749 +
1750 const formattedContentID = stringToChunk(contentSegmentID.toString(16));
1751 writeChunk(destination, boundaryID);
1752 writeChunk(destination, completeBoundaryScript2);
@@ -1760,10 +1774,13 @@ export function writeClientRenderBoundaryInstruction(
1774 // Future calls can just reuse the same function.
1775 writeChunk(destination, clientRenderScript1Partial);
1776 }
1763 - invariant(
1764 - boundaryID !== null,
1765 - 'An ID must have been assigned before we can complete the boundary.',
1766 - );
1777 +
1778 + if (boundaryID === null) {
1779 + throw new Error(
1780 + 'An ID must have been assigned before we can complete the boundary.',
1781 + );
1782 + }
1783 +
1784 writeChunk(destination, boundaryID);
1785 return writeChunk(destination, clientRenderScript2);
1786 }
packages/react-dom/src/server/ReactDOMServerLegacyPartialRendererBrowser.js
+2 -5
@@ -6,21 +6,18 @@
6 */
7
8 import ReactVersion from 'shared/ReactVersion';
9 -import invariant from 'shared/invariant';
9
10 import {renderToString, renderToStaticMarkup} from './ReactDOMStringRenderer';
11
12 function renderToNodeStream() {
14 - invariant(
15 - false,
13 + throw new Error(
14 'ReactDOMServer.renderToNodeStream(): The streaming API is not available ' +
15 'in the browser. Use ReactDOMServer.renderToString() instead.',
16 );
17 }
18
19 function renderToStaticNodeStream() {
22 - invariant(
23 - false,
20 + throw new Error(
21 'ReactDOMServer.renderToStaticNodeStream(): The streaming API is not available ' +
22 'in the browser. Use ReactDOMServer.renderToStaticMarkup() instead.',
23 );
packages/react-dom/src/server/ReactPartialRenderer.js
+57 -47
@@ -13,7 +13,6 @@ import type {LazyComponent} from 'react/src/ReactLazy';
13 import type {ReactProvider, ReactContext} from 'shared/ReactTypes';
14
15 import * as React from 'react';
16 -import invariant from 'shared/invariant';
16 import isArray from 'shared/isArray';
17 import getComponentNameFromType from 'shared/getComponentNameFromType';
18 import {describeUnknownElementTypeFrameInDEV} from 'shared/ReactComponentStackFrame';
@@ -213,7 +212,10 @@ const VALID_TAG_REGEX = /^[a-zA-Z][a-zA-Z:_\.\-\d]*$/; // Simplified subset
212 const validatedTagCache = {};
213 function validateDangerousTag(tag) {
214 if (!validatedTagCache.hasOwnProperty(tag)) {
216 - invariant(VALID_TAG_REGEX.test(tag), 'Invalid tag: %s', tag);
215 + if (!VALID_TAG_REGEX.test(tag)) {
216 + throw new Error(`Invalid tag: ${tag}`);
217 + }
218 +
219 validatedTagCache[tag] = true;
220 }
221 }
@@ -729,12 +731,12 @@ function resolve(
731 if (typeof childContextTypes === 'object') {
732 childContext = inst.getChildContext();
733 for (const contextKey in childContext) {
732 - invariant(
733 - contextKey in childContextTypes,
734 - '%s.getChildContext(): key "%s" is not defined in childContextTypes.',
735 - getComponentNameFromType(Component) || 'Unknown',
736 - contextKey,
737 - );
734 + if (!(contextKey in childContextTypes)) {
735 + throw new Error(
736 + `${getComponentNameFromType(Component) ||
737 + 'Unknown'}.getChildContext(): key "${contextKey}" is not defined in childContextTypes.`,
738 + );
739 + }
740 }
741 } else {
742 if (__DEV__) {
@@ -944,11 +946,14 @@ class ReactDOMServerRenderer {
946 suspended = false;
947 // If rendering was suspended at this boundary, render the fallbackFrame
948 const fallbackFrame = frame.fallbackFrame;
947 - invariant(
948 - fallbackFrame,
949 - 'ReactDOMServer did not find an internal fallback frame for Suspense. ' +
950 - 'This is a bug in React. Please file an issue.',
951 - );
949 +
950 + if (!fallbackFrame) {
951 + throw new Error(
952 + 'ReactDOMServer did not find an internal fallback frame for Suspense. ' +
953 + 'This is a bug in React. Please file an issue.',
954 + );
955 + }
956 +
957 this.stack.push(fallbackFrame);
958 out[this.suspenseDepth] += '<!--$!-->';
959 // Skip flushing output since we're switching to the fallback
@@ -975,17 +980,19 @@ class ReactDOMServerRenderer {
980 } catch (err) {
981 if (err != null && typeof err.then === 'function') {
982 if (enableSuspenseServerRenderer) {
978 - invariant(
979 - this.suspenseDepth > 0,
980 - // TODO: include component name. This is a bit tricky with current factoring.
981 - 'A React component suspended while rendering, but no fallback UI was specified.\n' +
982 - '\n' +
983 - 'Add a <Suspense fallback=...> component higher in the tree to ' +
984 - 'provide a loading indicator or placeholder to display.',
985 - );
983 + if (this.suspenseDepth <= 0) {
984 + throw new Error(
985 + // TODO: include component name. This is a bit tricky with current factoring.
986 + 'A React component suspended while rendering, but no fallback UI was specified.\n' +
987 + '\n' +
988 + 'Add a <Suspense fallback=...> component higher in the tree to ' +
989 + 'provide a loading indicator or placeholder to display.',
990 + );
991 + }
992 +
993 suspended = true;
994 } else {
988 - invariant(false, 'ReactDOMServer does not yet support Suspense.');
995 + throw new Error('ReactDOMServer does not yet support Suspense.');
996 }
997 } else {
998 throw err;
@@ -1035,17 +1042,18 @@ class ReactDOMServerRenderer {
1042 if (nextChild != null && nextChild.$$typeof != null) {
1043 // Catch unexpected special types early.
1044 const $$typeof = nextChild.$$typeof;
1038 - invariant(
1039 - $$typeof !== REACT_PORTAL_TYPE,
1040 - 'Portals are not currently supported by the server renderer. ' +
1041 - 'Render them conditionally so that they only appear on the client render.',
1042 - );
1045 +
1046 + if ($$typeof === REACT_PORTAL_TYPE) {
1047 + throw new Error(
1048 + 'Portals are not currently supported by the server renderer. ' +
1049 + 'Render them conditionally so that they only appear on the client render.',
1050 + );
1051 + }
1052 +
1053 // Catch-all to prevent an infinite loop if React.Children.toArray() supports some new type.
1044 - invariant(
1045 - false,
1046 - 'Unknown element-like object type: %s. This is likely a bug in React. ' +
1054 + throw new Error(
1055 + `Unknown element-like object type: ${($$typeof: any).toString()}. This is likely a bug in React. ` +
1056 'Please file an issue.',
1048 - ($$typeof: any).toString(),
1057 );
1058 }
1059 const nextChildren = toArray(nextChild);
@@ -1134,7 +1142,7 @@ class ReactDOMServerRenderer {
1142 this.suspenseDepth++;
1143 return '<!--$-->';
1144 } else {
1137 - invariant(false, 'ReactDOMServer does not yet support Suspense.');
1145 + throw new Error('ReactDOMServer does not yet support Suspense.');
1146 }
1147 }
1148 // eslint-disable-next-line-no-fallthrough
@@ -1157,8 +1165,7 @@ class ReactDOMServerRenderer {
1165 this.stack.push(frame);
1166 return '';
1167 }
1160 - invariant(
1161 - false,
1168 + throw new Error(
1169 'ReactDOMServer does not yet support scope components.',
1170 );
1171 }
@@ -1338,13 +1345,13 @@ class ReactDOMServerRenderer {
1345 info += '\n\nCheck the render method of `' + ownerName + '`.';
1346 }
1347 }
1341 - invariant(
1342 - false,
1348 +
1349 + throw new Error(
1350 'Element type is invalid: expected a string (for built-in ' +
1351 'components) or a class/function (for composite components) ' +
1345 - 'but got: %s.%s',
1346 - elementType == null ? elementType : typeof elementType,
1347 - info,
1352 + `but got: ${
1353 + elementType == null ? elementType : typeof elementType
1354 + }.${info}`,
1355 );
1356 }
1357 }
@@ -1464,15 +1471,18 @@ class ReactDOMServerRenderer {
1471 'children on <textarea>.',
1472 );
1473 }
1467 - invariant(
1468 - defaultValue == null,
1469 - 'If you supply `defaultValue` on a <textarea>, do not pass children.',
1470 - );
1471 - if (isArray(textareaChildren)) {
1472 - invariant(
1473 - textareaChildren.length <= 1,
1474 - '<textarea> can only have at most one child.',
1474 +
1475 + if (defaultValue != null) {
1476 + throw new Error(
1477 + 'If you supply `defaultValue` on a <textarea>, do not pass children.',
1478 );
1479 + }
1480 +
1481 + if (isArray(textareaChildren)) {
1482 + if (textareaChildren.length > 1) {
1483 + throw new Error('<textarea> can only have at most one child.');
1484 + }
1485 +
1486 textareaChildren = textareaChildren[0];
1487 }
1488
packages/react-dom/src/server/ReactPartialRendererHooks.js
+21 -20
@@ -19,7 +19,6 @@ import type PartialRenderer from './ReactPartialRenderer';
19
20 import {validateContextBounds} from './ReactPartialRendererContext';
21
22 -import invariant from 'shared/invariant';
22 import {enableCache} from 'shared/ReactFeatureFlags';
23 import is from 'shared/objectIs';
24
@@ -63,15 +62,17 @@ let isInHookUserCodeInDev = false;
62 let currentHookNameInDev: ?string;
63
64 function resolveCurrentlyRenderingComponent(): Object {
66 - invariant(
67 - currentlyRenderingComponent !== null,
68 - 'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for' +
69 - ' one of the following reasons:\n' +
70 - '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' +
71 - '2. You might be breaking the Rules of Hooks\n' +
72 - '3. You might have more than one copy of React in the same app\n' +
73 - 'See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.',
74 - );
65 + if (currentlyRenderingComponent === null) {
66 + throw new Error(
67 + 'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for' +
68 + ' one of the following reasons:\n' +
69 + '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' +
70 + '2. You might be breaking the Rules of Hooks\n' +
71 + '3. You might have more than one copy of React in the same app\n' +
72 + 'See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.',
73 + );
74 + }
75 +
76 if (__DEV__) {
77 if (isInHookUserCodeInDev) {
78 console.error(
@@ -127,7 +128,7 @@ function areHookInputsEqual(
128
129 function createHook(): Hook {
130 if (numberOfReRenders > 0) {
130 - invariant(false, 'Rendered more hooks than during the previous render');
131 + throw new Error('Rendered more hooks than during the previous render');
132 }
133 return {
134 memoizedState: null,
@@ -216,7 +217,7 @@ export function resetHooksState(): void {
217 }
218
219 function getCacheForType<T>(resourceType: () => T): T {
219 - invariant(false, 'Not implemented.');
220 + throw new Error('Not implemented.');
221 }
222
223 function readContext<T>(context: ReactContext<T>): T {
@@ -423,11 +424,12 @@ function dispatchAction<A>(
424 queue: UpdateQueue<A>,
425 action: A,
426 ) {
426 - invariant(
427 - numberOfReRenders < RE_RENDER_LIMIT,
428 - 'Too many re-renders. React limits the number of renders to prevent ' +
429 - 'an infinite loop.',
430 - );
427 + if (numberOfReRenders >= RE_RENDER_LIMIT) {
428 + throw new Error(
429 + 'Too many re-renders. React limits the number of renders to prevent ' +
430 + 'an infinite loop.',
431 + );
432 + }
433
434 if (componentIdentity === currentlyRenderingComponent) {
435 // This is a render phase update. Stash it in a lazily-created map of
@@ -484,8 +486,7 @@ function useSyncExternalStore<T>(
486 getServerSnapshot?: () => T,
487 ): T {
488 if (getServerSnapshot === undefined) {
487 - invariant(
488 - false,
489 + throw new Error(
490 'Missing getServerSnapshot, which is required for ' +
491 'server-rendered content. Will revert to client rendering.',
492 );
@@ -515,7 +516,7 @@ function useOpaqueIdentifier(): OpaqueIDType {
516 }
517
518 function useCacheRefresh(): <T>(?() => T, ?T) => void {
518 - invariant(false, 'Not implemented.');
519 + throw new Error('Not implemented.');
520 }
521
522 function noop(): void {}
packages/react-dom/src/server/ReactThreadIDAllocator.js
+10 -9
@@ -11,8 +11,6 @@
11 // indices can be used to reference a tightly packed array. As opposed to being used in a Map.
12 // The first allocated index is 1.
13
14 -import invariant from 'shared/invariant';
15 -
14 export type ThreadID = number;
15
16 let nextAvailableThreadIDs = new Uint16Array(16);
@@ -25,13 +23,16 @@ function growThreadCountAndReturnNextAvailable() {
23 const oldArray = nextAvailableThreadIDs;
24 const oldSize = oldArray.length;
25 const newSize = oldSize * 2;
28 - invariant(
29 - newSize <= 0x10000,
30 - 'Maximum number of concurrent React renderers exceeded. ' +
31 - 'This can happen if you are not properly destroying the Readable provided by React. ' +
32 - 'Ensure that you call .destroy() on it if you no longer want to read from it, ' +
33 - 'and did not read to the end. If you use .pipe() this should be automatic.',
34 - );
26 +
27 + if (newSize > 0x10000) {
28 + throw new Error(
29 + 'Maximum number of concurrent React renderers exceeded. ' +
30 + 'This can happen if you are not properly destroying the Readable provided by React. ' +
31 + 'Ensure that you call .destroy() on it if you no longer want to read from it, ' +
32 + 'and did not read to the end. If you use .pipe() this should be automatic.',
33 + );
34 + }
35 +
36 const newArray = new Uint16Array(newSize);
37 newArray.set(oldArray);
38 nextAvailableThreadIDs = newArray;
packages/react-dom/src/shared/assertValidProps.js
+30 -25
@@ -5,8 +5,6 @@
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 -import invariant from 'shared/invariant';
9 -
8 import voidElementTags from './voidElementTags';
9
10 const HTML = '__html';
@@ -17,25 +15,30 @@ function assertValidProps(tag: string, props: ?Object) {
15 }
16 // Note the use of `==` which checks for null or undefined.
17 if (voidElementTags[tag]) {
20 - invariant(
21 - props.children == null && props.dangerouslySetInnerHTML == null,
22 - '%s is a void element tag and must neither have `children` nor ' +
23 - 'use `dangerouslySetInnerHTML`.',
24 - tag,
25 - );
18 + if (props.children != null || props.dangerouslySetInnerHTML != null) {
19 + throw new Error(
20 + `${tag} is a void element tag and must neither have \`children\` nor ` +
21 + 'use `dangerouslySetInnerHTML`.',
22 + );
23 + }
24 }
25 if (props.dangerouslySetInnerHTML != null) {
28 - invariant(
29 - props.children == null,
30 - 'Can only set one of `children` or `props.dangerouslySetInnerHTML`.',
31 - );
32 - invariant(
33 - typeof props.dangerouslySetInnerHTML === 'object' &&
34 - HTML in props.dangerouslySetInnerHTML,
35 - '`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. ' +
36 - 'Please visit https://reactjs.org/link/dangerously-set-inner-html ' +
37 - 'for more information.',
38 - );
26 + if (props.children != null) {
27 + throw new Error(
28 + 'Can only set one of `children` or `props.dangerouslySetInnerHTML`.',
29 + );
30 + }
31 +
32 + if (
33 + typeof props.dangerouslySetInnerHTML !== 'object' ||
34 + !(HTML in props.dangerouslySetInnerHTML)
35 + ) {
36 + throw new Error(
37 + '`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. ' +
38 + 'Please visit https://reactjs.org/link/dangerously-set-inner-html ' +
39 + 'for more information.',
40 + );
41 + }
42 }
43 if (__DEV__) {
44 if (
@@ -51,12 +54,14 @@ function assertValidProps(tag: string, props: ?Object) {
54 );
55 }
56 }
54 - invariant(
55 - props.style == null || typeof props.style === 'object',
56 - 'The `style` prop expects a mapping from style properties to values, ' +
57 - "not a string. For example, style={{marginRight: spacing + 'em'}} when " +
58 - 'using JSX.',
59 - );
57 +
58 + if (props.style != null && typeof props.style !== 'object') {
59 + throw new Error(
60 + 'The `style` prop expects a mapping from style properties to values, ' +
61 + "not a string. For example, style={{marginRight: spacing + 'em'}} when " +
62 + 'using JSX.',
63 + );
64 + }
65 }
66
67 export default assertValidProps;
packages/react-dom/src/shared/sanitizeURL.js
+5 -5
@@ -7,7 +7,6 @@
7 * @flow
8 */
9
10 -import invariant from 'shared/invariant';
10 import {disableJavaScriptURLs} from 'shared/ReactFeatureFlags';
11
12 // A javascript: URL can contain leading C0 control or \u0020 SPACE,
@@ -26,10 +25,11 @@ let didWarn = false;
25
26 function sanitizeURL(url: string) {
27 if (disableJavaScriptURLs) {
29 - invariant(
30 - !isJavaScriptProtocol.test(url),
31 - 'React has blocked a javascript: URL as a security precaution.',
32 - );
28 + if (isJavaScriptProtocol.test(url)) {
29 + throw new Error(
30 + 'React has blocked a javascript: URL as a security precaution.',
31 + );
32 + }
33 } else if (__DEV__) {
34 if (!didWarn && isJavaScriptProtocol.test(url)) {
35 didWarn = true;
packages/react-dom/src/test-utils/ReactTestUtils.js
+32 -29
@@ -16,7 +16,6 @@ import {
16 HostText,
17 } from 'react-reconciler/src/ReactWorkTags';
18 import {SyntheticEvent} from '../events/SyntheticEvent';
19 -import invariant from 'shared/invariant';
19 import {ELEMENT_NODE} from '../shared/HTMLNodeType';
20 import {
21 rethrowCaughtError,
@@ -105,12 +104,10 @@ function validateClassInstance(inst, methodName) {
104 } else {
105 received = stringified;
106 }
108 - invariant(
109 - false,
110 - '%s(...): the first argument must be a React class instance. ' +
111 - 'Instead received: %s.',
112 - methodName,
113 - received,
107 +
108 + throw new Error(
109 + `${methodName}(...): the first argument must be a React class instance. ` +
110 + `Instead received: ${received}.`,
111 );
112 }
113
@@ -197,11 +194,13 @@ function scryRenderedDOMComponentsWithClass(root, classNames) {
194 const classList = className.split(/\s+/);
195
196 if (!isArray(classNames)) {
200 - invariant(
201 - classNames !== undefined,
202 - 'TestUtils.scryRenderedDOMComponentsWithClass expects a ' +
203 - 'className as a second argument.',
204 - );
197 + if (classNames === undefined) {
198 + throw new Error(
199 + 'TestUtils.scryRenderedDOMComponentsWithClass expects a ' +
200 + 'className as a second argument.',
201 + );
202 + }
203 +
204 classNames = classNames.split(/\s+/);
205 }
206 return classNames.every(function(name) {
@@ -478,12 +477,13 @@ function getListener(inst: Fiber, registrationName: string) {
477 if (shouldPreventMouseEvent(registrationName, inst.type, props)) {
478 return null;
479 }
481 - invariant(
482 - !listener || typeof listener === 'function',
483 - 'Expected `%s` listener to be a function, instead got a value of `%s` type.',
484 - registrationName,
485 - typeof listener,
486 - );
480 +
481 + if (listener && typeof listener !== 'function') {
482 + throw new Error(
483 + `Expected \`${registrationName}\` listener to be a function, instead got a value of \`${typeof listener}\` type.`,
484 + );
485 + }
486 +
487 return listener;
488 }
489
@@ -564,17 +564,20 @@ const directDispatchEventTypes = new Set([
564 */
565 function makeSimulator(eventType) {
566 return function(domNode, eventData) {
567 - invariant(
568 - !React.isValidElement(domNode),
569 - 'TestUtils.Simulate expected a DOM node as the first argument but received ' +
570 - 'a React element. Pass the DOM node you wish to simulate the event on instead. ' +
571 - 'Note that TestUtils.Simulate will not work if you are using shallow rendering.',
572 - );
573 - invariant(
574 - !isCompositeComponent(domNode),
575 - 'TestUtils.Simulate expected a DOM node as the first argument but received ' +
576 - 'a component instance. Pass the DOM node you wish to simulate the event on instead.',
577 - );
567 + if (React.isValidElement(domNode)) {
568 + throw new Error(
569 + 'TestUtils.Simulate expected a DOM node as the first argument but received ' +
570 + 'a React element. Pass the DOM node you wish to simulate the event on instead. ' +
571 + 'Note that TestUtils.Simulate will not work if you are using shallow rendering.',
572 + );
573 + }
574 +
575 + if (isCompositeComponent(domNode)) {
576 + throw new Error(
577 + 'TestUtils.Simulate expected a DOM node as the first argument but received ' +
578 + 'a component instance. Pass the DOM node you wish to simulate the event on instead.',
579 + );
580 + }
581
582 const reactName = 'on' + eventType[0].toUpperCase() + eventType.slice(1);
583 const fakeNativeEvent = new Event();
packages/react-fetch/src/ReactFetchBrowser.js
+1
@@ -129,6 +129,7 @@ function preloadRecord(url: string, options: mixed): Record {
129 if (options.method || options.body || options.signal) {
130 // TODO: wire up our own cancellation mechanism.
131 // TODO: figure out what to do with POST.
132 + // eslint-disable-next-line react-internal/prod-error-codes
133 throw Error('Unsupported option');
134 }
135 }
packages/react-fetch/src/ReactFetchNode.js
+1
@@ -177,6 +177,7 @@ function preloadRecord(url: string, options: mixed): Record<FetchResponse> {
177 if (options.method || options.body || options.signal) {
178 // TODO: wire up our own cancellation mechanism.
179 // TODO: figure out what to do with POST.
180 + // eslint-disable-next-line react-internal/prod-error-codes
181 throw Error('Unsupported option');
182 }
183 }
packages/react-native-renderer/src/ReactFabricComponentTree.js
+5 -6
@@ -5,18 +5,17 @@
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 -import invariant from 'shared/invariant';
9 -
8 function getInstanceFromInstance(instanceHandle) {
9 return instanceHandle;
10 }
11
12 function getTagFromInstance(inst) {
13 const nativeInstance = inst.stateNode.canonical;
16 - invariant(
17 - nativeInstance._nativeTag,
18 - 'All native instances should have a tag.',
19 - );
14 +
15 + if (!nativeInstance._nativeTag) {
16 + throw new Error('All native instances should have a tag.');
17 + }
18 +
19 return nativeInstance;
20 }
21
packages/react-native-renderer/src/ReactNativeBridgeEventPlugin.js
+8 -6
@@ -10,7 +10,6 @@
10 import type {AnyNativeEvent} from './legacy-events/PluginModuleType';
11 import type {TopLevelType} from './legacy-events/TopLevelEventTypes';
12 import SyntheticEvent from './legacy-events/SyntheticEvent';
13 -import invariant from 'shared/invariant';
13
14 // Module provided by RN:
15 import {ReactNativeViewConfigRegistry} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface';
@@ -148,11 +147,14 @@ const ReactNativeBridgeEventPlugin = {
147 }
148 const bubbleDispatchConfig = customBubblingEventTypes[topLevelType];
149 const directDispatchConfig = customDirectEventTypes[topLevelType];
151 - invariant(
152 - bubbleDispatchConfig || directDispatchConfig,
153 - 'Unsupported top level event type "%s" dispatched',
154 - topLevelType,
155 - );
150 +
151 + if (!bubbleDispatchConfig && !directDispatchConfig) {
152 + throw new Error(
153 + // $FlowFixMe - Flow doesn't like this string coercion because DOMTopLevelEventType is opaque
154 + `Unsupported top level event type "${topLevelType}" dispatched`,
155 + );
156 + }
157 +
158 const event = SyntheticEvent.getPooled(
159 bubbleDispatchConfig || directDispatchConfig,
160 targetInst,
packages/react-native-renderer/src/ReactNativeComponentTree.js
+5 -3
@@ -5,8 +5,6 @@
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 -import invariant from 'shared/invariant';
9 -
8 const instanceCache = new Map();
9 const instanceProps = new Map();
10
@@ -30,7 +28,11 @@ function getTagFromInstance(inst) {
28 nativeInstance = nativeInstance.canonical;
29 tag = nativeInstance._nativeTag;
30 }
33 - invariant(tag, 'All native instances should have a tag.');
31 +
32 + if (!tag) {
33 + throw new Error('All native instances should have a tag.');
34 + }
35 +
36 return nativeInstance;
37 }
38
packages/react-native-renderer/src/ReactNativeFiberInspector.js
+2 -5
@@ -16,7 +16,6 @@ import {
16 } from 'react-reconciler/src/ReactFiberTreeReflection';
17 import getComponentNameFromType from 'shared/getComponentNameFromType';
18 import {HostComponent} from 'react-reconciler/src/ReactWorkTags';
19 -import invariant from 'shared/invariant';
19 // Module provided by RN:
20 import {UIManager} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface';
21 import {enableGetInspectorDataForInstanceInProduction} from 'shared/ReactFeatureFlags';
@@ -250,8 +249,7 @@ if (__DEV__) {
249 };
250 } else {
251 getInspectorDataForViewTag = () => {
253 - invariant(
254 - false,
252 + throw new Error(
253 'getInspectorDataForViewTag() is not available in production',
254 );
255 };
@@ -263,8 +261,7 @@ if (__DEV__) {
261 locationY: number,
262 callback: (viewData: TouchedViewDataAtPoint) => mixed,
263 ): void => {
266 - invariant(
267 - false,
264 + throw new Error(
265 'getInspectorDataForViewAtPoint() is not available in production.',
266 );
267 };
packages/react-native-renderer/src/ReactNativeGetListener.js
+7 -7
@@ -8,7 +8,6 @@
8
9 import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
10
11 -import invariant from 'shared/invariant';
11 import {getFiberCurrentPropsFromNode} from './legacy-events/EventPluginUtils';
12
13 export default function getListener(
@@ -26,11 +25,12 @@ export default function getListener(
25 return null;
26 }
27 const listener = props[registrationName];
29 - invariant(
30 - !listener || typeof listener === 'function',
31 - 'Expected `%s` listener to be a function, instead got a value of `%s` type.',
32 - registrationName,
33 - typeof listener,
34 - );
28 +
29 + if (listener && typeof listener !== 'function') {
30 + throw new Error(
31 + `Expected \`${registrationName}\` listener to be a function, instead got a value of \`${typeof listener}\` type.`,
32 + );
33 + }
34 +
35 return listener;
36 }
packages/react-native-renderer/src/ReactNativeHostConfig.js
+6 -10
@@ -9,8 +9,6 @@
9
10 import type {TouchedViewDataAtPoint} from './ReactNativeTypes';
11
12 -import invariant from 'shared/invariant';
13 -
12 // Modules provided by RN:
13 import {
14 ReactNativeViewConfigRegistry,
@@ -147,10 +145,9 @@ export function createTextInstance(
145 hostContext: HostContext,
146 internalInstanceHandle: Object,
147 ): TextInstance {
150 - invariant(
151 - hostContext.isInAParentText,
152 - 'Text strings must be rendered within a <Text> component.',
153 - );
148 + if (!hostContext.isInAParentText) {
149 + throw new Error('Text strings must be rendered within a <Text> component.');
150 + }
151
152 const tag = allocateTag();
153
@@ -411,10 +408,9 @@ export function insertInContainerBefore(
408 // We create a wrapper object for the container in ReactNative render()
409 // Or we refactor to remove wrapper objects entirely.
410 // For more info on pros/cons see PR #8560 description.
414 - invariant(
415 - typeof parentInstance !== 'number',
416 - 'Container does not support insertBefore operation',
417 - );
411 + if (typeof parentInstance === 'number') {
412 + throw new Error('Container does not support insertBefore operation');
413 + }
414 }
415
416 export function removeChild(
packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/InitializeNativeFabricUIManager.js
+44 -43
@@ -11,8 +11,6 @@
11
12 // Mock of the Native Hooks
13
14 -import invariant from 'shared/invariant';
15 -
14 const roots = new Map();
15 const allocatedTags = new Set();
16
@@ -54,11 +52,10 @@ const RCTFabricUIManager = {
52 props,
53 eventTarget,
54 ) {
57 - invariant(
58 - !allocatedTags.has(reactTag),
59 - 'Created two native views with tag %s',
60 - reactTag,
61 - );
55 + if (allocatedTags.has(reactTag)) {
56 + throw new Error(`Created two native views with tag ${reactTag}`);
57 + }
58 +
59 allocatedTags.add(reactTag);
60 return {
61 reactTag: reactTag,
@@ -127,27 +124,29 @@ const RCTFabricUIManager = {
124 registerEventHandler: jest.fn(function registerEventHandler(callback) {}),
125
126 measure: jest.fn(function measure(node, callback) {
130 - invariant(
131 - typeof node === 'object',
132 - 'Expected node to be an object, was passed "%s"',
133 - typeof node,
134 - );
135 - invariant(
136 - typeof node.viewName === 'string',
137 - 'Expected node to be a host node.',
138 - );
127 + if (typeof node !== 'object') {
128 + throw new Error(
129 + `Expected node to be an object, was passed "${typeof node}"`,
130 + );
131 + }
132 +
133 + if (typeof node.viewName !== 'string') {
134 + throw new Error('Expected node to be a host node.');
135 + }
136 +
137 callback(10, 10, 100, 100, 0, 0);
138 }),
139 measureInWindow: jest.fn(function measureInWindow(node, callback) {
142 - invariant(
143 - typeof node === 'object',
144 - 'Expected node to be an object, was passed "%s"',
145 - typeof node,
146 - );
147 - invariant(
148 - typeof node.viewName === 'string',
149 - 'Expected node to be a host node.',
150 - );
140 + if (typeof node !== 'object') {
141 + throw new Error(
142 + `Expected node to be an object, was passed "${typeof node}"`,
143 + );
144 + }
145 +
146 + if (typeof node.viewName !== 'string') {
147 + throw new Error('Expected node to be a host node.');
148 + }
149 +
150 callback(10, 10, 100, 100);
151 }),
152 measureLayout: jest.fn(function measureLayout(
@@ -156,24 +155,26 @@ const RCTFabricUIManager = {
155 fail,
156 success,
157 ) {
159 - invariant(
160 - typeof node === 'object',
161 - 'Expected node to be an object, was passed "%s"',
162 - typeof node,
163 - );
164 - invariant(
165 - typeof node.viewName === 'string',
166 - 'Expected node to be a host node.',
167 - );
168 - invariant(
169 - typeof relativeNode === 'object',
170 - 'Expected relative node to be an object, was passed "%s"',
171 - typeof relativeNode,
172 - );
173 - invariant(
174 - typeof relativeNode.viewName === 'string',
175 - 'Expected relative node to be a host node.',
176 - );
158 + if (typeof node !== 'object') {
159 + throw new Error(
160 + `Expected node to be an object, was passed "${typeof node}"`,
161 + );
162 + }
163 +
164 + if (typeof node.viewName !== 'string') {
165 + throw new Error('Expected node to be a host node.');
166 + }
167 +
168 + if (typeof relativeNode !== 'object') {
169 + throw new Error(
170 + `Expected relative node to be an object, was passed "${typeof relativeNode}"`,
171 + );
172 + }
173 +
174 + if (typeof relativeNode.viewName !== 'string') {
175 + throw new Error('Expected relative node to be a host node.');
176 + }
177 +
178 success(1, 1, 100, 100);
179 }),
180 setIsJSResponder: jest.fn(),
packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/ReactNativeViewConfigRegistry.js
+30 -27
@@ -16,8 +16,6 @@ import type {
16 ViewConfigGetter,
17 } from './ReactNativeTypes';
18
19 -import invariant from 'shared/invariant';
20 -
19 // Event configs
20 const customBubblingEventTypes = {};
21 const customDirectEventTypes = {};
@@ -38,11 +36,11 @@ function processEventTypes(
36 if (__DEV__) {
37 if (bubblingEventTypes != null && directEventTypes != null) {
38 for (const topLevelType in directEventTypes) {
41 - invariant(
42 - bubblingEventTypes[topLevelType] == null,
43 - 'Event cannot be both direct and bubbling: %s',
44 - topLevelType,
45 - );
39 + if (bubblingEventTypes[topLevelType] != null) {
40 + throw new Error(
41 + `Event cannot be both direct and bubbling: ${topLevelType}`,
42 + );
43 + }
44 }
45 }
46 }
@@ -73,17 +71,18 @@ function processEventTypes(
71 * This is done to avoid causing Prepack deopts.
72 */
73 exports.register = function(name: string, callback: ViewConfigGetter): string {
76 - invariant(
77 - !viewConfigCallbacks.has(name),
78 - 'Tried to register two views with the same name %s',
79 - name,
80 - );
81 - invariant(
82 - typeof callback === 'function',
83 - 'View config getter callback for component `%s` must be a function (received `%s`)',
84 - name,
85 - callback === null ? 'null' : typeof callback,
86 - );
74 + if (viewConfigCallbacks.has(name)) {
75 + throw new Error(`Tried to register two views with the same name ${name}`);
76 + }
77 +
78 + if (typeof callback !== 'function') {
79 + throw new Error(
80 + `View config getter callback for component \`${name}\` must be a function (received \`${
81 + callback === null ? 'null' : typeof callback
82 + }\`)`,
83 + );
84 + }
85 +
86 viewConfigCallbacks.set(name, callback);
87 return name;
88 };
@@ -98,14 +97,14 @@ exports.get = function(name: string): ReactNativeBaseComponentViewConfig<> {
97 if (!viewConfigs.has(name)) {
98 const callback = viewConfigCallbacks.get(name);
99 if (typeof callback !== 'function') {
101 - invariant(
102 - false,
103 - 'View config getter callback for component `%s` must be a function (received `%s`).%s',
104 - name,
105 - callback === null ? 'null' : typeof callback,
106 - typeof name[0] === 'string' && /[a-z]/.test(name[0])
107 - ? ' Make sure to start component names with a capital letter.'
108 - : '',
100 + throw new Error(
101 + `View config getter callback for component \`${name}\` must be a function (received \`${
102 + callback === null ? 'null' : typeof callback
103 + }\`).${
104 + typeof name[0] === 'string' && /[a-z]/.test(name[0])
105 + ? ' Make sure to start component names with a capital letter.'
106 + : ''
107 + }`,
108 );
109 }
110 viewConfig = callback();
@@ -118,6 +117,10 @@ exports.get = function(name: string): ReactNativeBaseComponentViewConfig<> {
117 } else {
118 viewConfig = viewConfigs.get(name);
119 }
121 - invariant(viewConfig, 'View config not found for name %s', name);
120 +
121 + if (!viewConfig) {
122 + throw new Error(`View config not found for name ${name}`);
123 + }
124 +
125 return viewConfig;
126 };
packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/UIManager.js
+60 -57
@@ -11,8 +11,6 @@
11
12 // Mock of the Native Hooks
13
14 -import invariant from 'shared/invariant';
15 -
14 // Map of viewTag -> {children: [childTag], parent: ?parentTag}
15 const roots = [];
16 const views = new Map();
@@ -33,18 +31,21 @@ function autoCreateRoot(tag) {
31 function insertSubviewAtIndex(parent, child, index) {
32 const parentInfo = views.get(parent);
33 const childInfo = views.get(child);
36 - invariant(
37 - childInfo.parent === null,
38 - 'Inserting view %s %s which already has parent',
39 - child,
40 - JSON.stringify(childInfo.props),
41 - );
42 - invariant(
43 - 0 <= index && index <= parentInfo.children.length,
44 - 'Invalid index %s for children %s',
45 - index,
46 - parentInfo.children,
47 - );
34 +
35 + if (childInfo.parent !== null) {
36 + throw new Error(
37 + `Inserting view ${child} ${JSON.stringify(
38 + childInfo.props,
39 + )} which already has parent`,
40 + );
41 + }
42 +
43 + if (0 > index || index > parentInfo.children.length) {
44 + throw new Error(
45 + `Invalid index ${index} for children ${parentInfo.children}`,
46 + );
47 + }
48 +
49 parentInfo.children.splice(index, 0, child);
50 childInfo.parent = parent;
51 }
@@ -53,7 +54,11 @@ function removeChild(parent, child) {
54 const parentInfo = views.get(parent);
55 const childInfo = views.get(child);
56 const index = parentInfo.children.indexOf(child);
56 - invariant(index >= 0, 'Missing view %s during removal', child);
57 +
58 + if (index < 0) {
59 + throw new Error(`Missing view ${child} during removal`);
60 + }
61 +
62 parentInfo.children.splice(index, 1);
63 childInfo.parent = null;
64 }
@@ -75,11 +80,10 @@ const RCTUIManager = {
80 },
81 clearJSResponder: jest.fn(),
82 createView: jest.fn(function createView(reactTag, viewName, rootTag, props) {
78 - invariant(
79 - !views.has(reactTag),
80 - 'Created two native views with tag %s',
81 - reactTag,
82 - );
83 + if (views.has(reactTag)) {
84 + throw new Error(`Created two native views with tag ${reactTag}`);
85 + }
86 +
87 views.set(reactTag, {
88 children: [],
89 parent: null,
@@ -92,12 +96,12 @@ const RCTUIManager = {
96 setJSResponder: jest.fn(),
97 setChildren: jest.fn(function setChildren(parentTag, reactTags) {
98 autoCreateRoot(parentTag);
99 +
100 // Native doesn't actually check this but it seems like a good idea
96 - invariant(
97 - views.get(parentTag).children.length === 0,
98 - 'Calling .setChildren on nonempty view %s',
99 - parentTag,
100 - );
101 + if (views.get(parentTag).children.length !== 0) {
102 + throw new Error(`Calling .setChildren on nonempty view ${parentTag}`);
103 + }
104 +
105 // This logic ported from iOS (RCTUIManager.m)
106 reactTags.forEach((tag, i) => {
107 insertSubviewAtIndex(parentTag, tag, i);
@@ -112,19 +116,20 @@ const RCTUIManager = {
116 removeAtIndices = [],
117 ) {
118 autoCreateRoot(parentTag);
119 +
120 // This logic ported from iOS (RCTUIManager.m)
116 - invariant(
117 - moveFromIndices.length === moveToIndices.length,
118 - 'Mismatched move indices %s and %s',
119 - moveFromIndices,
120 - moveToIndices,
121 - );
122 - invariant(
123 - addChildReactTags.length === addAtIndices.length,
124 - 'Mismatched add indices %s and %s',
125 - addChildReactTags,
126 - addAtIndices,
127 - );
121 + if (moveFromIndices.length !== moveToIndices.length) {
122 + throw new Error(
123 + `Mismatched move indices ${moveFromIndices} and ${moveToIndices}`,
124 + );
125 + }
126 +
127 + if (addChildReactTags.length !== addAtIndices.length) {
128 + throw new Error(
129 + `Mismatched add indices ${addChildReactTags} and ${addAtIndices}`,
130 + );
131 + }
132 +
133 const parentInfo = views.get(parentTag);
134 const permanentlyRemovedChildren = removeAtIndices.map(
135 index => parentInfo.children[index],
@@ -157,19 +162,17 @@ const RCTUIManager = {
162 }),
163 replaceExistingNonRootView: jest.fn(),
164 measure: jest.fn(function measure(tag, callback) {
160 - invariant(
161 - typeof tag === 'number',
162 - 'Expected tag to be a number, was passed %s',
163 - tag,
164 - );
165 + if (typeof tag !== 'number') {
166 + throw new Error(`Expected tag to be a number, was passed ${tag}`);
167 + }
168 +
169 callback(10, 10, 100, 100, 0, 0);
170 }),
171 measureInWindow: jest.fn(function measureInWindow(tag, callback) {
168 - invariant(
169 - typeof tag === 'number',
170 - 'Expected tag to be a number, was passed %s',
171 - tag,
172 - );
172 + if (typeof tag !== 'number') {
173 + throw new Error(`Expected tag to be a number, was passed ${tag}`);
174 + }
175 +
176 callback(10, 10, 100, 100);
177 }),
178 measureLayout: jest.fn(function measureLayout(
@@ -178,16 +181,16 @@ const RCTUIManager = {
181 fail,
182 success,
183 ) {
181 - invariant(
182 - typeof tag === 'number',
183 - 'Expected tag to be a number, was passed %s',
184 - tag,
185 - );
186 - invariant(
187 - typeof relativeTag === 'number',
188 - 'Expected relativeTag to be a number, was passed %s',
189 - relativeTag,
190 - );
184 + if (typeof tag !== 'number') {
185 + throw new Error(`Expected tag to be a number, was passed ${tag}`);
186 + }
187 +
188 + if (typeof relativeTag !== 'number') {
189 + throw new Error(
190 + `Expected relativeTag to be a number, was passed ${relativeTag}`,
191 + );
192 + }
193 +
194 success(1, 1, 100, 100);
195 }),
196 __takeSnapshot: jest.fn(),
packages/react-native-renderer/src/legacy-events/EventBatching.js
+8 -6
@@ -6,7 +6,6 @@
6 * @flow
7 */
8
9 -import invariant from 'shared/invariant';
9 import {rethrowCaughtError} from 'shared/ReactErrorUtils';
10
11 import type {ReactSyntheticEvent} from './ReactSyntheticEventType';
@@ -56,11 +55,14 @@ export function runEventsInBatch(
55 }
56
57 forEachAccumulated(processingEventQueue, executeDispatchesAndReleaseTopLevel);
59 - invariant(
60 - !eventQueue,
61 - 'processEventQueue(): Additional events were enqueued while processing ' +
62 - 'an event queue. Support for this has not yet been implemented.',
63 - );
58 +
59 + if (eventQueue) {
60 + throw new Error(
61 + 'processEventQueue(): Additional events were enqueued while processing ' +
62 + 'an event queue. Support for this has not yet been implemented.',
63 + );
64 + }
65 +
66 // This would be a good time to rethrow if any of the event handlers threw.
67 rethrowCaughtError();
68 }
packages/react-native-renderer/src/legacy-events/EventPluginRegistry.js
+52 -44
@@ -14,8 +14,6 @@ import type {
14 LegacyPluginModule,
15 } from './PluginModuleType';
16
17 -import invariant from 'shared/invariant';
18 -
17 type NamesToPlugins = {
18 [key: PluginName]: LegacyPluginModule<AnyNativeEvent>,
19 ...,
@@ -45,34 +43,39 @@ function recomputePluginOrdering(): void {
43 for (const pluginName in namesToPlugins) {
44 const pluginModule = namesToPlugins[pluginName];
45 const pluginIndex = eventPluginOrder.indexOf(pluginName);
48 - invariant(
49 - pluginIndex > -1,
50 - 'EventPluginRegistry: Cannot inject event plugins that do not exist in ' +
51 - 'the plugin ordering, `%s`.',
52 - pluginName,
53 - );
46 +
47 + if (pluginIndex <= -1) {
48 + throw new Error(
49 + 'EventPluginRegistry: Cannot inject event plugins that do not exist in ' +
50 + `the plugin ordering, \`${pluginName}\`.`,
51 + );
52 + }
53 +
54 if (plugins[pluginIndex]) {
55 continue;
56 }
57 - invariant(
58 - pluginModule.extractEvents,
59 - 'EventPluginRegistry: Event plugins must implement an `extractEvents` ' +
60 - 'method, but `%s` does not.',
61 - pluginName,
62 - );
57 +
58 + if (!pluginModule.extractEvents) {
59 + throw new Error(
60 + 'EventPluginRegistry: Event plugins must implement an `extractEvents` ' +
61 + `method, but \`${pluginName}\` does not.`,
62 + );
63 + }
64 +
65 plugins[pluginIndex] = pluginModule;
66 const publishedEvents = pluginModule.eventTypes;
67 for (const eventName in publishedEvents) {
66 - invariant(
67 - publishEventForPlugin(
68 + if (
69 + !publishEventForPlugin(
70 publishedEvents[eventName],
71 pluginModule,
72 eventName,
71 - ),
72 - 'EventPluginRegistry: Failed to publish event `%s` for plugin `%s`.',
73 - eventName,
74 - pluginName,
75 - );
73 + )
74 + ) {
75 + throw new Error(
76 + `EventPluginRegistry: Failed to publish event \`${eventName}\` for plugin \`${pluginName}\`.`,
77 + );
78 + }
79 }
80 }
81 }
@@ -90,12 +93,13 @@ function publishEventForPlugin(
93 pluginModule: LegacyPluginModule<AnyNativeEvent>,
94 eventName: string,
95 ): boolean {
93 - invariant(
94 - !eventNameDispatchConfigs.hasOwnProperty(eventName),
95 - 'EventPluginRegistry: More than one plugin attempted to publish the same ' +
96 - 'event name, `%s`.',
97 - eventName,
98 - );
96 + if (eventNameDispatchConfigs.hasOwnProperty(eventName)) {
97 + throw new Error(
98 + 'EventPluginRegistry: More than one plugin attempted to publish the same ' +
99 + `event name, \`${eventName}\`.`,
100 + );
101 + }
102 +
103 eventNameDispatchConfigs[eventName] = dispatchConfig;
104
105 const phasedRegistrationNames = dispatchConfig.phasedRegistrationNames;
@@ -134,12 +138,13 @@ function publishRegistrationName(
138 pluginModule: LegacyPluginModule<AnyNativeEvent>,
139 eventName: string,
140 ): void {
137 - invariant(
138 - !registrationNameModules[registrationName],
139 - 'EventPluginRegistry: More than one plugin attempted to publish the same ' +
140 - 'registration name, `%s`.',
141 - registrationName,
142 - );
141 + if (registrationNameModules[registrationName]) {
142 + throw new Error(
143 + 'EventPluginRegistry: More than one plugin attempted to publish the same ' +
144 + `registration name, \`${registrationName}\`.`,
145 + );
146 + }
147 +
148 registrationNameModules[registrationName] = pluginModule;
149 registrationNameDependencies[registrationName] =
150 pluginModule.eventTypes[eventName].dependencies;
@@ -198,11 +203,13 @@ export const possibleRegistrationNames = __DEV__ ? {} : (null: any);
203 export function injectEventPluginOrder(
204 injectedEventPluginOrder: EventPluginOrder,
205 ): void {
201 - invariant(
202 - !eventPluginOrder,
203 - 'EventPluginRegistry: Cannot inject event plugin ordering more than ' +
204 - 'once. You are likely trying to load more than one copy of React.',
205 - );
206 + if (eventPluginOrder) {
207 + throw new Error(
208 + 'EventPluginRegistry: Cannot inject event plugin ordering more than ' +
209 + 'once. You are likely trying to load more than one copy of React.',
210 + );
211 + }
212 +
213 // Clone the ordering so it cannot be dynamically mutated.
214 eventPluginOrder = Array.prototype.slice.call(injectedEventPluginOrder);
215 recomputePluginOrdering();
@@ -230,12 +237,13 @@ export function injectEventPluginsByName(
237 !namesToPlugins.hasOwnProperty(pluginName) ||
238 namesToPlugins[pluginName] !== pluginModule
239 ) {
233 - invariant(
234 - !namesToPlugins[pluginName],
235 - 'EventPluginRegistry: Cannot inject two different event plugins ' +
236 - 'using the same name, `%s`.',
237 - pluginName,
238 - );
240 + if (namesToPlugins[pluginName]) {
241 + throw new Error(
242 + 'EventPluginRegistry: Cannot inject two different event plugins ' +
243 + `using the same name, \`${pluginName}\`.`,
244 + );
245 + }
246 +
247 namesToPlugins[pluginName] = pluginModule;
248 isOrderingDirty = true;
249 }
packages/react-native-renderer/src/legacy-events/EventPluginUtils.js
+5 -5
@@ -6,7 +6,6 @@
6 */
7
8 import {invokeGuardedCallbackAndCatchFirstError} from 'shared/ReactErrorUtils';
9 -import invariant from 'shared/invariant';
9 import isArray from 'shared/isArray';
10
11 export let getFiberCurrentPropsFromNode = null;
@@ -150,10 +149,11 @@ export function executeDirectDispatch(event) {
149 }
150 const dispatchListener = event._dispatchListeners;
151 const dispatchInstance = event._dispatchInstances;
153 - invariant(
154 - !isArray(dispatchListener),
155 - 'executeDirectDispatch(...): Invalid `event`.',
156 - );
152 +
153 + if (isArray(dispatchListener)) {
154 + throw new Error('executeDirectDispatch(...): Invalid `event`.');
155 + }
156 +
157 event.currentTarget = dispatchListener
158 ? getNodeFromInstance(dispatchInstance)
159 : null;
packages/react-native-renderer/src/legacy-events/ResponderEventPlugin.js
+7 -7
@@ -29,7 +29,6 @@ import {
29 import accumulateInto from './accumulateInto';
30 import forEachAccumulated from './forEachAccumulated';
31 import {HostComponent} from 'react-reconciler/src/ReactWorkTags';
32 -import invariant from 'shared/invariant';
32
33 /**
34 * Instance of element that should respond to touch/move types of interactions,
@@ -249,12 +248,13 @@ function getListener(inst, registrationName) {
248 return null;
249 }
250 const listener = props[registrationName];
252 - invariant(
253 - !listener || typeof listener === 'function',
254 - 'Expected `%s` listener to be a function, instead got a value of `%s` type.',
255 - registrationName,
256 - typeof listener,
257 - );
251 +
252 + if (listener && typeof listener !== 'function') {
253 + throw new Error(
254 + `Expected \`${registrationName}\` listener to be a function, instead got a value of \`${typeof listener}\` type.`,
255 + );
256 + }
257 +
258 return listener;
259 }
260
packages/react-native-renderer/src/legacy-events/ResponderTouchHistoryStore.js
+4 -3
@@ -7,8 +7,6 @@
7 * @flow
8 */
9
10 -import invariant from 'shared/invariant';
11 -
10 import {isStartish, isMoveish, isEndish} from './ResponderTopLevelEventTypes';
11
12 /**
@@ -94,7 +92,10 @@ function resetTouchRecord(touchRecord: TouchRecord, touch: Touch): void {
92 }
93
94 function getTouchIdentifier({identifier}: Touch): number {
97 - invariant(identifier != null, 'Touch object is missing identifier.');
95 + if (identifier == null) {
96 + throw new Error('Touch object is missing identifier.');
97 + }
98 +
99 if (__DEV__) {
100 if (identifier > MAX_TOUCH_BANK) {
101 console.error(
packages/react-native-renderer/src/legacy-events/SyntheticEvent.js
+7 -6
@@ -7,8 +7,6 @@
7
8 /* eslint valid-typeof: 0 */
9
10 -import invariant from 'shared/invariant';
11 -
10 const EVENT_POOL_SIZE = 10;
11
12 /**
@@ -325,10 +323,13 @@ function createOrGetPooledEvent(
323
324 function releasePooledEvent(event) {
325 const EventConstructor = this;
328 - invariant(
329 - event instanceof EventConstructor,
330 - 'Trying to release an event instance into a pool of a different type.',
331 - );
326 +
327 + if (!(event instanceof EventConstructor)) {
328 + throw new Error(
329 + 'Trying to release an event instance into a pool of a different type.',
330 + );
331 + }
332 +
333 event.destructor();
334 if (EventConstructor.eventPool.length < EVENT_POOL_SIZE) {
335 EventConstructor.eventPool.push(event);
packages/react-native-renderer/src/legacy-events/accumulate.js
+5 -5
@@ -7,7 +7,6 @@
7 * @flow
8 */
9
10 -import invariant from 'shared/invariant';
10 import isArray from 'shared/isArray';
11
12 /**
@@ -21,10 +20,11 @@ function accumulate<T>(
20 current: ?(T | Array<T>),
21 next: T | Array<T>,
22 ): T | Array<T> {
24 - invariant(
25 - next != null,
26 - 'accumulate(...): Accumulated items must not be null or undefined.',
27 - );
23 + if (next == null) {
24 + throw new Error(
25 + 'accumulate(...): Accumulated items must not be null or undefined.',
26 + );
27 + }
28
29 if (current == null) {
30 return next;
packages/react-native-renderer/src/legacy-events/accumulateInto.js
+5 -5
@@ -7,7 +7,6 @@
7 * @flow
8 */
9
10 -import invariant from 'shared/invariant';
10 import isArray from 'shared/isArray';
11
12 /**
@@ -27,10 +26,11 @@ function accumulateInto<T>(
26 current: ?(Array<T> | T),
27 next: T | Array<T>,
28 ): T | Array<T> {
30 - invariant(
31 - next != null,
32 - 'accumulateInto(...): Accumulated items must not be null or undefined.',
33 - );
29 + if (next == null) {
30 + throw new Error(
31 + 'accumulateInto(...): Accumulated items must not be null or undefined.',
32 + );
33 + }
34
35 if (current == null) {
36 return next;
packages/react-native-renderer/src/server/ReactNativeServerFormatConfig.js
+7 -8
@@ -21,8 +21,6 @@ import {
21 stringToPrecomputedChunk,
22 } from 'react-server/src/ReactServerStreamConfig';
23
24 -import invariant from 'shared/invariant';
25 -
24 export const isPrimaryRenderer = true;
25
26 // Every list of children or string is null terminated.
@@ -116,10 +114,12 @@ export type OpaqueIDType = number;
114 export function makeServerID(
115 responseState: null | ResponseState,
116 ): OpaqueIDType {
119 - invariant(
120 - responseState !== null,
121 - 'Invalid hook call. Hooks can only be called inside of the body of a function component.',
122 - );
117 + if (responseState === null) {
118 + throw new Error(
119 + 'Invalid hook call. Hooks can only be called inside of the body of a function component.',
120 + );
121 + }
122 +
123 // TODO: This is not deterministic since it's created during render.
124 return responseState.nextOpaqueID++;
125 }
@@ -167,8 +167,7 @@ export function pushEndInstance(
167 // IDs are formatted as little endian Uint16
168 function formatID(id: number): Uint8Array {
169 if (id > 0xffff) {
170 - invariant(
171 - false,
170 + throw new Error(
171 'More boundaries or placeholders than we expected to ever emit.',
172 );
173 }
packages/react-pg/src/ReactPostgres.js
+2 -5
@@ -12,7 +12,6 @@ import type {Wakeable} from 'shared/ReactTypes';
12 import {unstable_getCacheForType} from 'react';
13 import {Pool as PostgresPool} from 'pg';
14 import {prepareValue} from 'pg/lib/utils';
15 -import invariant from 'shared/invariant';
15
16 const Pending = 0;
17 const Resolved = 1;
@@ -92,8 +91,7 @@ Pool.prototype.query = function(query: string, values?: Array<mixed>) {
91 nextMap = new Map();
92 innerMap.set(key, nextMap);
93 } else if (!(nextMap instanceof Map)) {
95 - invariant(
96 - false,
94 + throw new Error(
95 'This query has received more parameters than the last time ' +
96 'the same query was used. Always pass the exact number of ' +
97 'parameters that the query needs.',
@@ -113,8 +111,7 @@ Pool.prototype.query = function(query: string, values?: Array<mixed>) {
111 record = createRecordFromThenable(thenable);
112 innerMap.set(key, record);
113 } else if (record instanceof Map) {
116 - invariant(
117 - false,
114 + throw new Error(
115 'This query has received fewer parameters than the last time ' +
116 'the same query was used. Always pass the exact number of ' +
117 'parameters that the query needs.',
packages/react-reconciler/src/ReactChildFiber.new.js
+67 -51
@@ -22,7 +22,6 @@ import {
22 REACT_LAZY_TYPE,
23 } from 'shared/ReactSymbols';
24 import {ClassComponent, HostText, HostPortal, Fragment} from './ReactWorkTags';
25 -import invariant from 'shared/invariant';
25 import isArray from 'shared/isArray';
26 import {
27 warnAboutStringRefs,
@@ -69,11 +68,14 @@ if (__DEV__) {
68 if (!child._store || child._store.validated || child.key != null) {
69 return;
70 }
72 - invariant(
73 - typeof child._store === 'object',
74 - 'React Component in warnForMissingKey should have a _store. ' +
75 - 'This error is likely caused by a bug in React. Please file an issue.',
76 - );
71 +
72 + if (typeof child._store !== 'object') {
73 + throw new Error(
74 + 'React Component in warnForMissingKey should have a _store. ' +
75 + 'This error is likely caused by a bug in React. Please file an issue.',
76 + );
77 + }
78 +
79 child._store.validated = true;
80
81 const componentName = getComponentNameFromFiber(returnFiber) || 'Component';
@@ -149,21 +151,28 @@ function coerceRef(
151 let inst;
152 if (owner) {
153 const ownerFiber = ((owner: any): Fiber);
152 - invariant(
153 - ownerFiber.tag === ClassComponent,
154 - 'Function components cannot have string refs. ' +
155 - 'We recommend using useRef() instead. ' +
156 - 'Learn more about using refs safely here: ' +
157 - 'https://reactjs.org/link/strict-mode-string-ref',
158 - );
154 +
155 + if (ownerFiber.tag !== ClassComponent) {
156 + throw new Error(
157 + 'Function components cannot have string refs. ' +
158 + 'We recommend using useRef() instead. ' +
159 + 'Learn more about using refs safely here: ' +
160 + 'https://reactjs.org/link/strict-mode-string-ref',
161 + );
162 + }
163 +
164 inst = ownerFiber.stateNode;
165 }
161 - invariant(
162 - inst,
163 - 'Missing owner for string ref %s. This error is likely caused by a ' +
164 - 'bug in React. Please file an issue.',
165 - mixedRef,
166 - );
166 +
167 + if (!inst) {
168 + throw new Error(
169 + `Missing owner for string ref ${mixedRef}. This error is likely caused by a ` +
170 + 'bug in React. Please file an issue.',
171 + );
172 + }
173 + // Assigning this to a const so Flow knows it won't change in the closure
174 + const resolvedInst = inst;
175 +
176 if (__DEV__) {
177 checkPropStringCoercion(mixedRef, 'ref');
178 }
@@ -178,10 +187,10 @@ function coerceRef(
187 return current.ref;
188 }
189 const ref = function(value) {
181 - let refs = inst.refs;
190 + let refs = resolvedInst.refs;
191 if (refs === emptyRefsObject) {
192 // This is a lazy pooled frozen object, so we need to initialize.
184 - refs = inst.refs = {};
193 + refs = resolvedInst.refs = {};
194 }
195 if (value === null) {
196 delete refs[stringRef];
@@ -192,20 +201,22 @@ function coerceRef(
201 ref._stringRef = stringRef;
202 return ref;
203 } else {
195 - invariant(
196 - typeof mixedRef === 'string',
197 - 'Expected ref to be a function, a string, an object returned by React.createRef(), or null.',
198 - );
199 - invariant(
200 - element._owner,
201 - 'Element ref was specified as a string (%s) but no owner was set. This could happen for one of' +
202 - ' the following reasons:\n' +
203 - '1. You may be adding a ref to a function component\n' +
204 - "2. You may be adding a ref to a component that was not created inside a component's render method\n" +
205 - '3. You have multiple copies of React loaded\n' +
206 - 'See https://reactjs.org/link/refs-must-have-owner for more information.',
207 - mixedRef,
208 - );
204 + if (typeof mixedRef !== 'string') {
205 + throw new Error(
206 + 'Expected ref to be a function, a string, an object returned by React.createRef(), or null.',
207 + );
208 + }
209 +
210 + if (!element._owner) {
211 + throw new Error(
212 + `Element ref was specified as a string (${mixedRef}) but no owner was set. This could happen for one of` +
213 + ' the following reasons:\n' +
214 + '1. You may be adding a ref to a function component\n' +
215 + "2. You may be adding a ref to a component that was not created inside a component's render method\n" +
216 + '3. You have multiple copies of React loaded\n' +
217 + 'See https://reactjs.org/link/refs-must-have-owner for more information.',
218 + );
219 + }
220 }
221 }
222 return mixedRef;
@@ -213,14 +224,15 @@ function coerceRef(
224
225 function throwOnInvalidObjectType(returnFiber: Fiber, newChild: Object) {
226 const childString = Object.prototype.toString.call(newChild);
216 - invariant(
217 - false,
218 - 'Objects are not valid as a React child (found: %s). ' +
227 +
228 + throw new Error(
229 + `Objects are not valid as a React child (found: ${
230 + childString === '[object Object]'
231 + ? 'object with keys {' + Object.keys(newChild).join(', ') + '}'
232 + : childString
233 + }). ` +
234 'If you meant to render a collection of children, use an array ' +
235 'instead.',
221 - childString === '[object Object]'
222 - ? 'object with keys {' + Object.keys(newChild).join(', ') + '}'
223 - : childString,
236 );
237 }
238
@@ -887,11 +899,13 @@ function ChildReconciler(shouldTrackSideEffects) {
899 // but using the iterator instead.
900
901 const iteratorFn = getIteratorFn(newChildrenIterable);
890 - invariant(
891 - typeof iteratorFn === 'function',
892 - 'An object is not an iterable. This error is likely caused by a bug in ' +
893 - 'React. Please file an issue.',
894 - );
902 +
903 + if (typeof iteratorFn !== 'function') {
904 + throw new Error(
905 + 'An object is not an iterable. This error is likely caused by a bug in ' +
906 + 'React. Please file an issue.',
907 + );
908 + }
909
910 if (__DEV__) {
911 // We don't support rendering Generators because it's a mutation.
@@ -938,7 +952,10 @@ function ChildReconciler(shouldTrackSideEffects) {
952 }
953
954 const newChildren = iteratorFn.call(newChildrenIterable);
941 - invariant(newChildren != null, 'An iterable object provided no iterator.');
955 +
956 + if (newChildren == null) {
957 + throw new Error('An iterable object provided no iterator.');
958 + }
959
960 let resultingFirstChild: Fiber | null = null;
961 let previousNewFiber: Fiber | null = null;
@@ -1313,10 +1330,9 @@ export function cloneChildFibers(
1330 current: Fiber | null,
1331 workInProgress: Fiber,
1332 ): void {
1316 - invariant(
1317 - current === null || workInProgress.child === current.child,
1318 - 'Resuming work not yet implemented.',
1319 - );
1333 + if (current !== null && workInProgress.child !== current.child) {
1334 + throw new Error('Resuming work not yet implemented.');
1335 + }
1336
1337 if (workInProgress.child === null) {
1338 return;
packages/react-reconciler/src/ReactChildFiber.old.js
+67 -51
@@ -22,7 +22,6 @@ import {
22 REACT_LAZY_TYPE,
23 } from 'shared/ReactSymbols';
24 import {ClassComponent, HostText, HostPortal, Fragment} from './ReactWorkTags';
25 -import invariant from 'shared/invariant';
25 import isArray from 'shared/isArray';
26 import {
27 warnAboutStringRefs,
@@ -69,11 +68,14 @@ if (__DEV__) {
68 if (!child._store || child._store.validated || child.key != null) {
69 return;
70 }
72 - invariant(
73 - typeof child._store === 'object',
74 - 'React Component in warnForMissingKey should have a _store. ' +
75 - 'This error is likely caused by a bug in React. Please file an issue.',
76 - );
71 +
72 + if (typeof child._store !== 'object') {
73 + throw new Error(
74 + 'React Component in warnForMissingKey should have a _store. ' +
75 + 'This error is likely caused by a bug in React. Please file an issue.',
76 + );
77 + }
78 +
79 child._store.validated = true;
80
81 const componentName = getComponentNameFromFiber(returnFiber) || 'Component';
@@ -149,21 +151,28 @@ function coerceRef(
151 let inst;
152 if (owner) {
153 const ownerFiber = ((owner: any): Fiber);
152 - invariant(
153 - ownerFiber.tag === ClassComponent,
154 - 'Function components cannot have string refs. ' +
155 - 'We recommend using useRef() instead. ' +
156 - 'Learn more about using refs safely here: ' +
157 - 'https://reactjs.org/link/strict-mode-string-ref',
158 - );
154 +
155 + if (ownerFiber.tag !== ClassComponent) {
156 + throw new Error(
157 + 'Function components cannot have string refs. ' +
158 + 'We recommend using useRef() instead. ' +
159 + 'Learn more about using refs safely here: ' +
160 + 'https://reactjs.org/link/strict-mode-string-ref',
161 + );
162 + }
163 +
164 inst = ownerFiber.stateNode;
165 }
161 - invariant(
162 - inst,
163 - 'Missing owner for string ref %s. This error is likely caused by a ' +
164 - 'bug in React. Please file an issue.',
165 - mixedRef,
166 - );
166 +
167 + if (!inst) {
168 + throw new Error(
169 + `Missing owner for string ref ${mixedRef}. This error is likely caused by a ` +
170 + 'bug in React. Please file an issue.',
171 + );
172 + }
173 + // Assigning this to a const so Flow knows it won't change in the closure
174 + const resolvedInst = inst;
175 +
176 if (__DEV__) {
177 checkPropStringCoercion(mixedRef, 'ref');
178 }
@@ -178,10 +187,10 @@ function coerceRef(
187 return current.ref;
188 }
189 const ref = function(value) {
181 - let refs = inst.refs;
190 + let refs = resolvedInst.refs;
191 if (refs === emptyRefsObject) {
192 // This is a lazy pooled frozen object, so we need to initialize.
184 - refs = inst.refs = {};
193 + refs = resolvedInst.refs = {};
194 }
195 if (value === null) {
196 delete refs[stringRef];
@@ -192,20 +201,22 @@ function coerceRef(
201 ref._stringRef = stringRef;
202 return ref;
203 } else {
195 - invariant(
196 - typeof mixedRef === 'string',
197 - 'Expected ref to be a function, a string, an object returned by React.createRef(), or null.',
198 - );
199 - invariant(
200 - element._owner,
201 - 'Element ref was specified as a string (%s) but no owner was set. This could happen for one of' +
202 - ' the following reasons:\n' +
203 - '1. You may be adding a ref to a function component\n' +
204 - "2. You may be adding a ref to a component that was not created inside a component's render method\n" +
205 - '3. You have multiple copies of React loaded\n' +
206 - 'See https://reactjs.org/link/refs-must-have-owner for more information.',
207 - mixedRef,
208 - );
204 + if (typeof mixedRef !== 'string') {
205 + throw new Error(
206 + 'Expected ref to be a function, a string, an object returned by React.createRef(), or null.',
207 + );
208 + }
209 +
210 + if (!element._owner) {
211 + throw new Error(
212 + `Element ref was specified as a string (${mixedRef}) but no owner was set. This could happen for one of` +
213 + ' the following reasons:\n' +
214 + '1. You may be adding a ref to a function component\n' +
215 + "2. You may be adding a ref to a component that was not created inside a component's render method\n" +
216 + '3. You have multiple copies of React loaded\n' +
217 + 'See https://reactjs.org/link/refs-must-have-owner for more information.',
218 + );
219 + }
220 }
221 }
222 return mixedRef;
@@ -213,14 +224,15 @@ function coerceRef(
224
225 function throwOnInvalidObjectType(returnFiber: Fiber, newChild: Object) {
226 const childString = Object.prototype.toString.call(newChild);
216 - invariant(
217 - false,
218 - 'Objects are not valid as a React child (found: %s). ' +
227 +
228 + throw new Error(
229 + `Objects are not valid as a React child (found: ${
230 + childString === '[object Object]'
231 + ? 'object with keys {' + Object.keys(newChild).join(', ') + '}'
232 + : childString
233 + }). ` +
234 'If you meant to render a collection of children, use an array ' +
235 'instead.',
221 - childString === '[object Object]'
222 - ? 'object with keys {' + Object.keys(newChild).join(', ') + '}'
223 - : childString,
236 );
237 }
238
@@ -887,11 +899,13 @@ function ChildReconciler(shouldTrackSideEffects) {
899 // but using the iterator instead.
900
901 const iteratorFn = getIteratorFn(newChildrenIterable);
890 - invariant(
891 - typeof iteratorFn === 'function',
892 - 'An object is not an iterable. This error is likely caused by a bug in ' +
893 - 'React. Please file an issue.',
894 - );
902 +
903 + if (typeof iteratorFn !== 'function') {
904 + throw new Error(
905 + 'An object is not an iterable. This error is likely caused by a bug in ' +
906 + 'React. Please file an issue.',
907 + );
908 + }
909
910 if (__DEV__) {
911 // We don't support rendering Generators because it's a mutation.
@@ -938,7 +952,10 @@ function ChildReconciler(shouldTrackSideEffects) {
952 }
953
954 const newChildren = iteratorFn.call(newChildrenIterable);
941 - invariant(newChildren != null, 'An iterable object provided no iterator.');
955 +
956 + if (newChildren == null) {
957 + throw new Error('An iterable object provided no iterator.');
958 + }
959
960 let resultingFirstChild: Fiber | null = null;
961 let previousNewFiber: Fiber | null = null;
@@ -1313,10 +1330,9 @@ export function cloneChildFibers(
1330 current: Fiber | null,
1331 workInProgress: Fiber,
1332 ): void {
1316 - invariant(
1317 - current === null || workInProgress.child === current.child,
1318 - 'Resuming work not yet implemented.',
1319 - );
1333 + if (current !== null && workInProgress.child !== current.child) {
1334 + throw new Error('Resuming work not yet implemented.');
1335 + }
1336
1337 if (workInProgress.child === null) {
1338 return;
packages/react-reconciler/src/ReactFiber.new.js
+4 -7
@@ -17,7 +17,6 @@ import type {Lanes} from './ReactFiberLane.new';
17 import type {SuspenseInstance, Props} from './ReactFiberHostConfig';
18 import type {OffscreenProps} from './ReactFiberOffscreenComponent';
19
20 -import invariant from 'shared/invariant';
20 import {
21 createRootStrictEffectsByDefault,
22 enableCache,
@@ -565,13 +564,11 @@ export function createFiberFromTypeAndProps(
564 info += '\n\nCheck the render method of `' + ownerName + '`.';
565 }
566 }
568 - invariant(
569 - false,
567 +
568 + throw new Error(
569 'Element type is invalid: expected a string (for built-in ' +
570 'components) or a class/function (for composite components) ' +
572 - 'but got: %s.%s',
573 - type == null ? type : typeof type,
574 - info,
571 + `but got: ${type == null ? type : typeof type}.${info}`,
572 );
573 }
574 }
@@ -604,7 +601,7 @@ export function createOffscreenHostContainerFiber(
601 return fiber;
602 } else {
603 // Only implemented in persistent mode
607 - invariant(false, 'Not implemented.');
604 + throw new Error('Not implemented.');
605 }
606 }
607
packages/react-reconciler/src/ReactFiber.old.js
+4 -7
@@ -17,7 +17,6 @@ import type {Lanes} from './ReactFiberLane.old';
17 import type {SuspenseInstance, Props} from './ReactFiberHostConfig';
18 import type {OffscreenProps} from './ReactFiberOffscreenComponent';
19
20 -import invariant from 'shared/invariant';
20 import {
21 createRootStrictEffectsByDefault,
22 enableCache,
@@ -565,13 +564,11 @@ export function createFiberFromTypeAndProps(
564 info += '\n\nCheck the render method of `' + ownerName + '`.';
565 }
566 }
568 - invariant(
569 - false,
567 +
568 + throw new Error(
569 'Element type is invalid: expected a string (for built-in ' +
570 'components) or a class/function (for composite components) ' +
572 - 'but got: %s.%s',
573 - type == null ? type : typeof type,
574 - info,
571 + `but got: ${type == null ? type : typeof type}.${info}`,
572 );
573 }
574 }
@@ -604,7 +601,7 @@ export function createOffscreenHostContainerFiber(
601 return fiber;
602 } else {
603 // Only implemented in persistent mode
607 - invariant(false, 'Not implemented.');
604 + throw new Error('Not implemented.');
605 }
606 }
607
packages/react-reconciler/src/ReactFiberBeginWork.new.js
+20 -17
@@ -92,7 +92,6 @@ import {
92 enableSchedulingProfiler,
93 enablePersistentOffscreenHostContainer,
94 } from 'shared/ReactFeatureFlags';
95 -import invariant from 'shared/invariant';
95 import isArray from 'shared/isArray';
96 import shallowEqual from 'shared/shallowEqual';
97 import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
@@ -1050,6 +1049,7 @@ function updateClassComponent(
1049 case true: {
1050 workInProgress.flags |= DidCapture;
1051 workInProgress.flags |= ShouldCapture;
1052 + // eslint-disable-next-line react-internal/prod-error-codes
1053 const error = new Error('Simulated error coming from DevTools');
1054 const lane = pickArbitraryLane(renderLanes);
1055 workInProgress.lanes = mergeLanes(workInProgress.lanes, lane);
@@ -1264,12 +1264,15 @@ function pushHostRootContext(workInProgress) {
1264 function updateHostRoot(current, workInProgress, renderLanes) {
1265 pushHostRootContext(workInProgress);
1266 const updateQueue = workInProgress.updateQueue;
1267 - invariant(
1268 - current !== null && updateQueue !== null,
1269 - 'If the root does not have an updateQueue, we should have already ' +
1270 - 'bailed out. This error is likely caused by a bug in React. Please ' +
1271 - 'file an issue.',
1272 - );
1267 +
1268 + if (current === null || updateQueue === null) {
1269 + throw new Error(
1270 + 'If the root does not have an updateQueue, we should have already ' +
1271 + 'bailed out. This error is likely caused by a bug in React. Please ' +
1272 + 'file an issue.',
1273 + );
1274 + }
1275 +
1276 const nextProps = workInProgress.pendingProps;
1277 const prevState = workInProgress.memoizedState;
1278 const prevChildren = prevState.element;
@@ -1496,15 +1499,13 @@ function mountLazyComponent(
1499 hint = ' Did you wrap a component in React.lazy() more than once?';
1500 }
1501 }
1502 +
1503 // This message intentionally doesn't mention ForwardRef or MemoComponent
1504 // because the fact that it's a separate type of work is an
1505 // implementation detail.
1502 - invariant(
1503 - false,
1504 - 'Element type is invalid. Received a promise that resolves to: %s. ' +
1505 - 'Lazy element type must resolve to a class or function.%s',
1506 - Component,
1507 - hint,
1506 + throw new Error(
1507 + `Element type is invalid. Received a promise that resolves to: ${Component}. ` +
1508 + `Lazy element type must resolve to a class or function.${hint}`,
1509 );
1510 }
1511
@@ -3317,6 +3318,7 @@ function remountFiber(
3318 if (__DEV__) {
3319 const returnFiber = oldWorkInProgress.return;
3320 if (returnFiber === null) {
3321 + // eslint-disable-next-line react-internal/prod-error-codes
3322 throw new Error('Cannot swap the root fiber.');
3323 }
3324
@@ -3337,11 +3339,13 @@ function remountFiber(
3339 } else {
3340 let prevSibling = returnFiber.child;
3341 if (prevSibling === null) {
3342 + // eslint-disable-next-line react-internal/prod-error-codes
3343 throw new Error('Expected parent to have a child.');
3344 }
3345 while (prevSibling.sibling !== oldWorkInProgress) {
3346 prevSibling = prevSibling.sibling;
3347 if (prevSibling === null) {
3348 + // eslint-disable-next-line react-internal/prod-error-codes
3349 throw new Error('Expected to find the previous sibling.');
3350 }
3351 }
@@ -3836,11 +3840,10 @@ function beginWork(
3840 break;
3841 }
3842 }
3839 - invariant(
3840 - false,
3841 - 'Unknown unit of work tag (%s). This error is likely caused by a bug in ' +
3843 +
3844 + throw new Error(
3845 + `Unknown unit of work tag (${workInProgress.tag}). This error is likely caused by a bug in ` +
3846 'React. Please file an issue.',
3843 - workInProgress.tag,
3847 );
3848 }
3849
packages/react-reconciler/src/ReactFiberBeginWork.old.js
+20 -17
@@ -92,7 +92,6 @@ import {
92 enableSchedulingProfiler,
93 enablePersistentOffscreenHostContainer,
94 } from 'shared/ReactFeatureFlags';
95 -import invariant from 'shared/invariant';
95 import isArray from 'shared/isArray';
96 import shallowEqual from 'shared/shallowEqual';
97 import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
@@ -1050,6 +1049,7 @@ function updateClassComponent(
1049 case true: {
1050 workInProgress.flags |= DidCapture;
1051 workInProgress.flags |= ShouldCapture;
1052 + // eslint-disable-next-line react-internal/prod-error-codes
1053 const error = new Error('Simulated error coming from DevTools');
1054 const lane = pickArbitraryLane(renderLanes);
1055 workInProgress.lanes = mergeLanes(workInProgress.lanes, lane);
@@ -1264,12 +1264,15 @@ function pushHostRootContext(workInProgress) {
1264 function updateHostRoot(current, workInProgress, renderLanes) {
1265 pushHostRootContext(workInProgress);
1266 const updateQueue = workInProgress.updateQueue;
1267 - invariant(
1268 - current !== null && updateQueue !== null,
1269 - 'If the root does not have an updateQueue, we should have already ' +
1270 - 'bailed out. This error is likely caused by a bug in React. Please ' +
1271 - 'file an issue.',
1272 - );
1267 +
1268 + if (current === null || updateQueue === null) {
1269 + throw new Error(
1270 + 'If the root does not have an updateQueue, we should have already ' +
1271 + 'bailed out. This error is likely caused by a bug in React. Please ' +
1272 + 'file an issue.',
1273 + );
1274 + }
1275 +
1276 const nextProps = workInProgress.pendingProps;
1277 const prevState = workInProgress.memoizedState;
1278 const prevChildren = prevState.element;
@@ -1496,15 +1499,13 @@ function mountLazyComponent(
1499 hint = ' Did you wrap a component in React.lazy() more than once?';
1500 }
1501 }
1502 +
1503 // This message intentionally doesn't mention ForwardRef or MemoComponent
1504 // because the fact that it's a separate type of work is an
1505 // implementation detail.
1502 - invariant(
1503 - false,
1504 - 'Element type is invalid. Received a promise that resolves to: %s. ' +
1505 - 'Lazy element type must resolve to a class or function.%s',
1506 - Component,
1507 - hint,
1506 + throw new Error(
1507 + `Element type is invalid. Received a promise that resolves to: ${Component}. ` +
1508 + `Lazy element type must resolve to a class or function.${hint}`,
1509 );
1510 }
1511
@@ -3317,6 +3318,7 @@ function remountFiber(
3318 if (__DEV__) {
3319 const returnFiber = oldWorkInProgress.return;
3320 if (returnFiber === null) {
3321 + // eslint-disable-next-line react-internal/prod-error-codes
3322 throw new Error('Cannot swap the root fiber.');
3323 }
3324
@@ -3337,11 +3339,13 @@ function remountFiber(
3339 } else {
3340 let prevSibling = returnFiber.child;
3341 if (prevSibling === null) {
3342 + // eslint-disable-next-line react-internal/prod-error-codes
3343 throw new Error('Expected parent to have a child.');
3344 }
3345 while (prevSibling.sibling !== oldWorkInProgress) {
3346 prevSibling = prevSibling.sibling;
3347 if (prevSibling === null) {
3348 + // eslint-disable-next-line react-internal/prod-error-codes
3349 throw new Error('Expected to find the previous sibling.');
3350 }
3351 }
@@ -3836,11 +3840,10 @@ function beginWork(
3840 break;
3841 }
3842 }
3839 - invariant(
3840 - false,
3841 - 'Unknown unit of work tag (%s). This error is likely caused by a bug in ' +
3843 +
3844 + throw new Error(
3845 + `Unknown unit of work tag (${workInProgress.tag}). This error is likely caused by a bug in ` +
3846 'React. Please file an issue.',
3843 - workInProgress.tag,
3847 );
3848 }
3849
packages/react-reconciler/src/ReactFiberClassComponent.new.js
+1 -3
@@ -35,7 +35,6 @@ import {get as getInstance, set as setInstance} from 'shared/ReactInstanceMap';
35 import shallowEqual from 'shared/shallowEqual';
36 import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
37 import getComponentNameFromType from 'shared/getComponentNameFromType';
38 -import invariant from 'shared/invariant';
38 import isArray from 'shared/isArray';
39 import {REACT_CONTEXT_TYPE, REACT_PROVIDER_TYPE} from 'shared/ReactSymbols';
40 import {setIsStrictModeForDevtools} from './ReactFiberDevToolsHook.new';
@@ -148,8 +147,7 @@ if (__DEV__) {
147 Object.defineProperty(fakeInternalInstance, '_processChildContext', {
148 enumerable: false,
149 value: function() {
151 - invariant(
152 - false,
150 + throw new Error(
151 '_processChildContext is not available in React 16+. This likely ' +
152 'means you have multiple copies of React and are attempting to nest ' +
153 'a React 15 tree inside a React 16 tree using ' +
packages/react-reconciler/src/ReactFiberClassComponent.old.js
+1 -3
@@ -35,7 +35,6 @@ import {get as getInstance, set as setInstance} from 'shared/ReactInstanceMap';
35 import shallowEqual from 'shared/shallowEqual';
36 import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
37 import getComponentNameFromType from 'shared/getComponentNameFromType';
38 -import invariant from 'shared/invariant';
38 import isArray from 'shared/isArray';
39 import {REACT_CONTEXT_TYPE, REACT_PROVIDER_TYPE} from 'shared/ReactSymbols';
40 import {setIsStrictModeForDevtools} from './ReactFiberDevToolsHook.old';
@@ -148,8 +147,7 @@ if (__DEV__) {
147 Object.defineProperty(fakeInternalInstance, '_processChildContext', {
148 enumerable: false,
149 value: function() {
151 - invariant(
152 - false,
150 + throw new Error(
151 '_processChildContext is not available in React 16+. This likely ' +
152 'means you have multiple copies of React and are attempting to nest ' +
153 'a React 15 tree inside a React 16 tree using ' +
packages/react-reconciler/src/ReactFiberCommitWork.new.js
+23 -23
@@ -78,7 +78,6 @@ import {
78 Visibility,
79 } from './ReactFiberFlags';
80 import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
81 -import invariant from 'shared/invariant';
81 import {
82 resetCurrentFiber as resetCurrentDebugFiberInDEV,
83 setCurrentFiber as setCurrentDebugFiberInDEV,
@@ -473,8 +472,7 @@ function commitBeforeMutationEffectsOnFiber(finishedWork: Fiber) {
472 // Nothing to do for these component types
473 break;
474 default: {
476 - invariant(
477 - false,
475 + throw new Error(
476 'This unit of work tag should not have side-effects. This error is ' +
477 'likely caused by a bug in React. Please file an issue.',
478 );
@@ -945,8 +943,7 @@ function commitLayoutEffectOnFiber(
943 case LegacyHiddenComponent:
944 break;
945 default:
948 - invariant(
949 - false,
946 + throw new Error(
947 'This unit of work tag should not have side-effects. This error is ' +
948 'likely caused by a bug in React. Please file an issue.',
949 );
@@ -1432,8 +1429,8 @@ function commitContainer(finishedWork: Fiber) {
1429 return;
1430 }
1431 }
1435 - invariant(
1436 - false,
1432 +
1433 + throw new Error(
1434 'This unit of work tag should not have side-effects. This error is ' +
1435 'likely caused by a bug in React. Please file an issue.',
1436 );
@@ -1447,8 +1444,8 @@ function getHostParentFiber(fiber: Fiber): Fiber {
1444 }
1445 parent = parent.return;
1446 }
1450 - invariant(
1451 - false,
1447 +
1448 + throw new Error(
1449 'Expected to find a host parent. This error is likely caused by a bug ' +
1450 'in React. Please file an issue.',
1451 );
@@ -1535,8 +1532,7 @@ function commitPlacement(finishedWork: Fiber): void {
1532 break;
1533 // eslint-disable-next-line-no-fallthrough
1534 default:
1538 - invariant(
1539 - false,
1535 + throw new Error(
1536 'Invalid host parent fiber. This error is likely caused by a bug ' +
1537 'in React. Please file an issue.',
1538 );
@@ -1641,11 +1637,13 @@ function unmountHostComponents(
1637 if (!currentParentIsValid) {
1638 let parent = node.return;
1639 findParent: while (true) {
1644 - invariant(
1645 - parent !== null,
1646 - 'Expected to find a host parent. This error is likely caused by ' +
1647 - 'a bug in React. Please file an issue.',
1648 - );
1640 + if (parent === null) {
1641 + throw new Error(
1642 + 'Expected to find a host parent. This error is likely caused by ' +
1643 + 'a bug in React. Please file an issue.',
1644 + );
1645 + }
1646 +
1647 const parentStateNode = parent.stateNode;
1648 switch (parent.tag) {
1649 case HostComponent:
@@ -1915,11 +1913,13 @@ function commitWork(current: Fiber | null, finishedWork: Fiber): void {
1913 return;
1914 }
1915 case HostText: {
1918 - invariant(
1919 - finishedWork.stateNode !== null,
1920 - 'This should have a text node initialized. This error is likely ' +
1921 - 'caused by a bug in React. Please file an issue.',
1922 - );
1916 + if (finishedWork.stateNode === null) {
1917 + throw new Error(
1918 + 'This should have a text node initialized. This error is likely ' +
1919 + 'caused by a bug in React. Please file an issue.',
1920 + );
1921 + }
1922 +
1923 const textInstance: TextInstance = finishedWork.stateNode;
1924 const newText: string = finishedWork.memoizedProps;
1925 // For hydration we reuse the update path but we treat the oldProps
@@ -1965,8 +1965,8 @@ function commitWork(current: Fiber | null, finishedWork: Fiber): void {
1965 break;
1966 }
1967 }
1968 - invariant(
1969 - false,
1968 +
1969 + throw new Error(
1970 'This unit of work tag should not have side-effects. This error is ' +
1971 'likely caused by a bug in React. Please file an issue.',
1972 );
packages/react-reconciler/src/ReactFiberCommitWork.old.js
+23 -23
@@ -78,7 +78,6 @@ import {
78 Visibility,
79 } from './ReactFiberFlags';
80 import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
81 -import invariant from 'shared/invariant';
81 import {
82 resetCurrentFiber as resetCurrentDebugFiberInDEV,
83 setCurrentFiber as setCurrentDebugFiberInDEV,
@@ -473,8 +472,7 @@ function commitBeforeMutationEffectsOnFiber(finishedWork: Fiber) {
472 // Nothing to do for these component types
473 break;
474 default: {
476 - invariant(
477 - false,
475 + throw new Error(
476 'This unit of work tag should not have side-effects. This error is ' +
477 'likely caused by a bug in React. Please file an issue.',
478 );
@@ -945,8 +943,7 @@ function commitLayoutEffectOnFiber(
943 case LegacyHiddenComponent:
944 break;
945 default:
948 - invariant(
949 - false,
946 + throw new Error(
947 'This unit of work tag should not have side-effects. This error is ' +
948 'likely caused by a bug in React. Please file an issue.',
949 );
@@ -1432,8 +1429,8 @@ function commitContainer(finishedWork: Fiber) {
1429 return;
1430 }
1431 }
1435 - invariant(
1436 - false,
1432 +
1433 + throw new Error(
1434 'This unit of work tag should not have side-effects. This error is ' +
1435 'likely caused by a bug in React. Please file an issue.',
1436 );
@@ -1447,8 +1444,8 @@ function getHostParentFiber(fiber: Fiber): Fiber {
1444 }
1445 parent = parent.return;
1446 }
1450 - invariant(
1451 - false,
1447 +
1448 + throw new Error(
1449 'Expected to find a host parent. This error is likely caused by a bug ' +
1450 'in React. Please file an issue.',
1451 );
@@ -1535,8 +1532,7 @@ function commitPlacement(finishedWork: Fiber): void {
1532 break;
1533 // eslint-disable-next-line-no-fallthrough
1534 default:
1538 - invariant(
1539 - false,
1535 + throw new Error(
1536 'Invalid host parent fiber. This error is likely caused by a bug ' +
1537 'in React. Please file an issue.',
1538 );
@@ -1641,11 +1637,13 @@ function unmountHostComponents(
1637 if (!currentParentIsValid) {
1638 let parent = node.return;
1639 findParent: while (true) {
1644 - invariant(
1645 - parent !== null,
1646 - 'Expected to find a host parent. This error is likely caused by ' +
1647 - 'a bug in React. Please file an issue.',
1648 - );
1640 + if (parent === null) {
1641 + throw new Error(
1642 + 'Expected to find a host parent. This error is likely caused by ' +
1643 + 'a bug in React. Please file an issue.',
1644 + );
1645 + }
1646 +
1647 const parentStateNode = parent.stateNode;
1648 switch (parent.tag) {
1649 case HostComponent:
@@ -1915,11 +1913,13 @@ function commitWork(current: Fiber | null, finishedWork: Fiber): void {
1913 return;
1914 }
1915 case HostText: {
1918 - invariant(
1919 - finishedWork.stateNode !== null,
1920 - 'This should have a text node initialized. This error is likely ' +
1921 - 'caused by a bug in React. Please file an issue.',
1922 - );
1916 + if (finishedWork.stateNode === null) {
1917 + throw new Error(
1918 + 'This should have a text node initialized. This error is likely ' +
1919 + 'caused by a bug in React. Please file an issue.',
1920 + );
1921 + }
1922 +
1923 const textInstance: TextInstance = finishedWork.stateNode;
1924 const newText: string = finishedWork.memoizedProps;
1925 // For hydration we reuse the update path but we treat the oldProps
@@ -1965,8 +1965,8 @@ function commitWork(current: Fiber | null, finishedWork: Fiber): void {
1965 break;
1966 }
1967 }
1968 - invariant(
1969 - false,
1968 +
1969 + throw new Error(
1970 'This unit of work tag should not have side-effects. This error is ' +
1971 'likely caused by a bug in React. Please file an issue.',
1972 );
packages/react-reconciler/src/ReactFiberCompleteWork.new.js
+24 -20
@@ -73,7 +73,6 @@ import {
73 StaticMask,
74 MutationMask,
75 } from './ReactFiberFlags';
76 -import invariant from 'shared/invariant';
76
77 import {
78 createInstance,
@@ -897,11 +896,13 @@ function completeWork(
896 }
897 } else {
898 if (!newProps) {
900 - invariant(
901 - workInProgress.stateNode !== null,
902 - 'We must have new props for new mounts. This error is likely ' +
903 - 'caused by a bug in React. Please file an issue.',
904 - );
899 + if (workInProgress.stateNode === null) {
900 + throw new Error(
901 + 'We must have new props for new mounts. This error is likely ' +
902 + 'caused by a bug in React. Please file an issue.',
903 + );
904 + }
905 +
906 // This can happen when we abort work.
907 bubbleProperties(workInProgress);
908 return null;
@@ -973,11 +974,12 @@ function completeWork(
974 updateHostText(current, workInProgress, oldText, newText);
975 } else {
976 if (typeof newText !== 'string') {
976 - invariant(
977 - workInProgress.stateNode !== null,
978 - 'We must have new props for new mounts. This error is likely ' +
979 - 'caused by a bug in React. Please file an issue.',
980 - );
977 + if (workInProgress.stateNode === null) {
978 + throw new Error(
979 + 'We must have new props for new mounts. This error is likely ' +
980 + 'caused by a bug in React. Please file an issue.',
981 + );
982 + }
983 // This can happen when we abort work.
984 }
985 const rootContainerInstance = getRootHostContainer();
@@ -1007,11 +1009,14 @@ function completeWork(
1009 if (nextState !== null && nextState.dehydrated !== null) {
1010 if (current === null) {
1011 const wasHydrated = popHydrationState(workInProgress);
1010 - invariant(
1011 - wasHydrated,
1012 - 'A dehydrated suspense component was completed without a hydrated node. ' +
1013 - 'This is probably a bug in React.',
1014 - );
1012 +
1013 + if (!wasHydrated) {
1014 + throw new Error(
1015 + 'A dehydrated suspense component was completed without a hydrated node. ' +
1016 + 'This is probably a bug in React.',
1017 + );
1018 + }
1019 +
1020 prepareToHydrateHostSuspenseInstance(workInProgress);
1021 bubbleProperties(workInProgress);
1022 if (enableProfilerTimer) {
@@ -1478,11 +1483,10 @@ function completeWork(
1483 }
1484 }
1485 }
1481 - invariant(
1482 - false,
1483 - 'Unknown unit of work tag (%s). This error is likely caused by a bug in ' +
1486 +
1487 + throw new Error(
1488 + `Unknown unit of work tag (${workInProgress.tag}). This error is likely caused by a bug in ` +
1489 'React. Please file an issue.',
1485 - workInProgress.tag,
1490 );
1491 }
1492
packages/react-reconciler/src/ReactFiberCompleteWork.old.js
+24 -20
@@ -73,7 +73,6 @@ import {
73 StaticMask,
74 MutationMask,
75 } from './ReactFiberFlags';
76 -import invariant from 'shared/invariant';
76
77 import {
78 createInstance,
@@ -897,11 +896,13 @@ function completeWork(
896 }
897 } else {
898 if (!newProps) {
900 - invariant(
901 - workInProgress.stateNode !== null,
902 - 'We must have new props for new mounts. This error is likely ' +
903 - 'caused by a bug in React. Please file an issue.',
904 - );
899 + if (workInProgress.stateNode === null) {
900 + throw new Error(
901 + 'We must have new props for new mounts. This error is likely ' +
902 + 'caused by a bug in React. Please file an issue.',
903 + );
904 + }
905 +
906 // This can happen when we abort work.
907 bubbleProperties(workInProgress);
908 return null;
@@ -973,11 +974,12 @@ function completeWork(
974 updateHostText(current, workInProgress, oldText, newText);
975 } else {
976 if (typeof newText !== 'string') {
976 - invariant(
977 - workInProgress.stateNode !== null,
978 - 'We must have new props for new mounts. This error is likely ' +
979 - 'caused by a bug in React. Please file an issue.',
980 - );
977 + if (workInProgress.stateNode === null) {
978 + throw new Error(
979 + 'We must have new props for new mounts. This error is likely ' +
980 + 'caused by a bug in React. Please file an issue.',
981 + );
982 + }
983 // This can happen when we abort work.
984 }
985 const rootContainerInstance = getRootHostContainer();
@@ -1007,11 +1009,14 @@ function completeWork(
1009 if (nextState !== null && nextState.dehydrated !== null) {
1010 if (current === null) {
1011 const wasHydrated = popHydrationState(workInProgress);
1010 - invariant(
1011 - wasHydrated,
1012 - 'A dehydrated suspense component was completed without a hydrated node. ' +
1013 - 'This is probably a bug in React.',
1014 - );
1012 +
1013 + if (!wasHydrated) {
1014 + throw new Error(
1015 + 'A dehydrated suspense component was completed without a hydrated node. ' +
1016 + 'This is probably a bug in React.',
1017 + );
1018 + }
1019 +
1020 prepareToHydrateHostSuspenseInstance(workInProgress);
1021 bubbleProperties(workInProgress);
1022 if (enableProfilerTimer) {
@@ -1478,11 +1483,10 @@ function completeWork(
1483 }
1484 }
1485 }
1481 - invariant(
1482 - false,
1483 - 'Unknown unit of work tag (%s). This error is likely caused by a bug in ' +
1486 +
1487 + throw new Error(
1488 + `Unknown unit of work tag (${workInProgress.tag}). This error is likely caused by a bug in ` +
1489 'React. Please file an issue.',
1485 - workInProgress.tag,
1490 );
1491 }
1492
packages/react-reconciler/src/ReactFiberContext.new.js
+27 -24
@@ -14,7 +14,6 @@ import {isFiberMounted} from './ReactFiberTreeReflection';
14 import {disableLegacyContext} from 'shared/ReactFeatureFlags';
15 import {ClassComponent, HostRoot} from './ReactWorkTags';
16 import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
17 -import invariant from 'shared/invariant';
17 import checkPropTypes from 'shared/checkPropTypes';
18
19 import {createCursor, push, pop} from './ReactFiberStack.new';
@@ -161,11 +160,12 @@ function pushTopLevelContextObject(
160 if (disableLegacyContext) {
161 return;
162 } else {
164 - invariant(
165 - contextStackCursor.current === emptyContextObject,
166 - 'Unexpected context found on stack. ' +
167 - 'This error is likely caused by a bug in React. Please file an issue.',
168 - );
163 + if (contextStackCursor.current !== emptyContextObject) {
164 + throw new Error(
165 + 'Unexpected context found on stack. ' +
166 + 'This error is likely caused by a bug in React. Please file an issue.',
167 + );
168 + }
169
170 push(contextStackCursor, context, fiber);
171 push(didPerformWorkStackCursor, didChange, fiber);
@@ -205,12 +205,12 @@ function processChildContext(
205
206 const childContext = instance.getChildContext();
207 for (const contextKey in childContext) {
208 - invariant(
209 - contextKey in childContextTypes,
210 - '%s.getChildContext(): key "%s" is not defined in childContextTypes.',
211 - getComponentNameFromFiber(fiber) || 'Unknown',
212 - contextKey,
213 - );
208 + if (!(contextKey in childContextTypes)) {
209 + throw new Error(
210 + `${getComponentNameFromFiber(fiber) ||
211 + 'Unknown'}.getChildContext(): key "${contextKey}" is not defined in childContextTypes.`,
212 + );
213 + }
214 }
215 if (__DEV__) {
216 const name = getComponentNameFromFiber(fiber) || 'Unknown';
@@ -256,11 +256,13 @@ function invalidateContextProvider(
256 return;
257 } else {
258 const instance = workInProgress.stateNode;
259 - invariant(
260 - instance,
261 - 'Expected to have an instance by this point. ' +
262 - 'This error is likely caused by a bug in React. Please file an issue.',
263 - );
259 +
260 + if (!instance) {
261 + throw new Error(
262 + 'Expected to have an instance by this point. ' +
263 + 'This error is likely caused by a bug in React. Please file an issue.',
264 + );
265 + }
266
267 if (didChange) {
268 // Merge parent and own context.
@@ -293,11 +295,12 @@ function findCurrentUnmaskedContext(fiber: Fiber): Object {
295 } else {
296 // Currently this is only used with renderSubtreeIntoContainer; not sure if it
297 // makes sense elsewhere
296 - invariant(
297 - isFiberMounted(fiber) && fiber.tag === ClassComponent,
298 - 'Expected subtree parent to be a mounted class component. ' +
299 - 'This error is likely caused by a bug in React. Please file an issue.',
300 - );
298 + if (!isFiberMounted(fiber) || fiber.tag !== ClassComponent) {
299 + throw new Error(
300 + 'Expected subtree parent to be a mounted class component. ' +
301 + 'This error is likely caused by a bug in React. Please file an issue.',
302 + );
303 + }
304
305 let node = fiber;
306 do {
@@ -314,8 +317,8 @@ function findCurrentUnmaskedContext(fiber: Fiber): Object {
317 }
318 node = node.return;
319 } while (node !== null);
317 - invariant(
318 - false,
320 +
321 + throw new Error(
322 'Found unexpected detached subtree parent. ' +
323 'This error is likely caused by a bug in React. Please file an issue.',
324 );
packages/react-reconciler/src/ReactFiberContext.old.js
+27 -24
@@ -14,7 +14,6 @@ import {isFiberMounted} from './ReactFiberTreeReflection';
14 import {disableLegacyContext} from 'shared/ReactFeatureFlags';
15 import {ClassComponent, HostRoot} from './ReactWorkTags';
16 import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
17 -import invariant from 'shared/invariant';
17 import checkPropTypes from 'shared/checkPropTypes';
18
19 import {createCursor, push, pop} from './ReactFiberStack.old';
@@ -161,11 +160,12 @@ function pushTopLevelContextObject(
160 if (disableLegacyContext) {
161 return;
162 } else {
164 - invariant(
165 - contextStackCursor.current === emptyContextObject,
166 - 'Unexpected context found on stack. ' +
167 - 'This error is likely caused by a bug in React. Please file an issue.',
168 - );
163 + if (contextStackCursor.current !== emptyContextObject) {
164 + throw new Error(
165 + 'Unexpected context found on stack. ' +
166 + 'This error is likely caused by a bug in React. Please file an issue.',
167 + );
168 + }
169
170 push(contextStackCursor, context, fiber);
171 push(didPerformWorkStackCursor, didChange, fiber);
@@ -205,12 +205,12 @@ function processChildContext(
205
206 const childContext = instance.getChildContext();
207 for (const contextKey in childContext) {
208 - invariant(
209 - contextKey in childContextTypes,
210 - '%s.getChildContext(): key "%s" is not defined in childContextTypes.',
211 - getComponentNameFromFiber(fiber) || 'Unknown',
212 - contextKey,
213 - );
208 + if (!(contextKey in childContextTypes)) {
209 + throw new Error(
210 + `${getComponentNameFromFiber(fiber) ||
211 + 'Unknown'}.getChildContext(): key "${contextKey}" is not defined in childContextTypes.`,
212 + );
213 + }
214 }
215 if (__DEV__) {
216 const name = getComponentNameFromFiber(fiber) || 'Unknown';
@@ -256,11 +256,13 @@ function invalidateContextProvider(
256 return;
257 } else {
258 const instance = workInProgress.stateNode;
259 - invariant(
260 - instance,
261 - 'Expected to have an instance by this point. ' +
262 - 'This error is likely caused by a bug in React. Please file an issue.',
263 - );
259 +
260 + if (!instance) {
261 + throw new Error(
262 + 'Expected to have an instance by this point. ' +
263 + 'This error is likely caused by a bug in React. Please file an issue.',
264 + );
265 + }
266
267 if (didChange) {
268 // Merge parent and own context.
@@ -293,11 +295,12 @@ function findCurrentUnmaskedContext(fiber: Fiber): Object {
295 } else {
296 // Currently this is only used with renderSubtreeIntoContainer; not sure if it
297 // makes sense elsewhere
296 - invariant(
297 - isFiberMounted(fiber) && fiber.tag === ClassComponent,
298 - 'Expected subtree parent to be a mounted class component. ' +
299 - 'This error is likely caused by a bug in React. Please file an issue.',
300 - );
298 + if (!isFiberMounted(fiber) || fiber.tag !== ClassComponent) {
299 + throw new Error(
300 + 'Expected subtree parent to be a mounted class component. ' +
301 + 'This error is likely caused by a bug in React. Please file an issue.',
302 + );
303 + }
304
305 let node = fiber;
306 do {
@@ -314,8 +317,8 @@ function findCurrentUnmaskedContext(fiber: Fiber): Object {
317 }
318 node = node.return;
319 } while (node !== null);
317 - invariant(
318 - false,
320 +
321 + throw new Error(
322 'Found unexpected detached subtree parent. ' +
323 'This error is likely caused by a bug in React. Please file an issue.',
324 );
packages/react-reconciler/src/ReactFiberHooks.new.js
+56 -44
@@ -88,7 +88,6 @@ import {
88 isInterleavedUpdate,
89 } from './ReactFiberWorkLoop.new';
90
91 -import invariant from 'shared/invariant';
91 import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
92 import is from 'shared/objectIs';
93 import isArray from 'shared/isArray';
@@ -310,8 +309,7 @@ function warnOnHookMismatchInDev(currentHookName: HookType) {
309 }
310
311 function throwInvalidHookError() {
313 - invariant(
314 - false,
312 + throw new Error(
313 'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for' +
314 ' one of the following reasons:\n' +
315 '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' +
@@ -436,11 +434,13 @@ export function renderWithHooks<Props, SecondArg>(
434 let numberOfReRenders: number = 0;
435 do {
436 didScheduleRenderPhaseUpdateDuringThisPass = false;
439 - invariant(
440 - numberOfReRenders < RE_RENDER_LIMIT,
441 - 'Too many re-renders. React limits the number of renders to prevent ' +
442 - 'an infinite loop.',
443 - );
437 +
438 + if (numberOfReRenders >= RE_RENDER_LIMIT) {
439 + throw new Error(
440 + 'Too many re-renders. React limits the number of renders to prevent ' +
441 + 'an infinite loop.',
442 + );
443 + }
444
445 numberOfReRenders += 1;
446 if (__DEV__) {
@@ -516,11 +516,12 @@ export function renderWithHooks<Props, SecondArg>(
516
517 didScheduleRenderPhaseUpdate = false;
518
519 - invariant(
520 - !didRenderTooFewHooks,
521 - 'Rendered fewer hooks than expected. This may be caused by an accidental ' +
522 - 'early return statement.',
523 - );
519 + if (didRenderTooFewHooks) {
520 + throw new Error(
521 + 'Rendered fewer hooks than expected. This may be caused by an accidental ' +
522 + 'early return statement.',
523 + );
524 + }
525
526 if (enableLazyContextPropagation) {
527 if (current !== null) {
@@ -669,10 +670,10 @@ function updateWorkInProgressHook(): Hook {
670 } else {
671 // Clone from the current hook.
672
672 - invariant(
673 - nextCurrentHook !== null,
674 - 'Rendered more hooks than during the previous render.',
675 - );
673 + if (nextCurrentHook === null) {
674 + throw new Error('Rendered more hooks than during the previous render.');
675 + }
676 +
677 currentHook = nextCurrentHook;
678
679 const newHook: Hook = {
@@ -745,10 +746,12 @@ function updateReducer<S, I, A>(
746 ): [S, Dispatch<A>] {
747 const hook = updateWorkInProgressHook();
748 const queue = hook.queue;
748 - invariant(
749 - queue !== null,
750 - 'Should have a queue. This is likely a bug in React. Please file an issue.',
751 - );
749 +
750 + if (queue === null) {
751 + throw new Error(
752 + 'Should have a queue. This is likely a bug in React. Please file an issue.',
753 + );
754 + }
755
756 queue.lastRenderedReducer = reducer;
757
@@ -900,10 +903,12 @@ function rerenderReducer<S, I, A>(
903 ): [S, Dispatch<A>] {
904 const hook = updateWorkInProgressHook();
905 const queue = hook.queue;
903 - invariant(
904 - queue !== null,
905 - 'Should have a queue. This is likely a bug in React. Please file an issue.',
906 - );
906 +
907 + if (queue === null) {
908 + throw new Error(
909 + 'Should have a queue. This is likely a bug in React. Please file an issue.',
910 + );
911 + }
912
913 queue.lastRenderedReducer = reducer;
914
@@ -1037,8 +1042,7 @@ function readFromUnsubscribedMutableSource<Source, Snapshot>(
1042
1043 // We expect this error not to be thrown during the synchronous retry,
1044 // because we blocked interleaved mutations.
1040 - invariant(
1041 - false,
1045 + throw new Error(
1046 'Cannot read from mutable source during the current render without tearing. This may be a bug in React. Please file an issue.',
1047 );
1048 }
@@ -1051,10 +1055,12 @@ function useMutableSource<Source, Snapshot>(
1055 subscribe: MutableSourceSubscribeFn<Source, Snapshot>,
1056 ): Snapshot {
1057 const root = ((getWorkInProgressRoot(): any): FiberRoot);
1054 - invariant(
1055 - root !== null,
1056 - 'Expected a work-in-progress root. This is a bug in React. Please file an issue.',
1057 - );
1058 +
1059 + if (root === null) {
1060 + throw new Error(
1061 + 'Expected a work-in-progress root. This is a bug in React. Please file an issue.',
1062 + );
1063 + }
1064
1065 const getVersion = source._getVersion;
1066 const version = getVersion(source._source);
@@ -1242,8 +1248,7 @@ function mountSyncExternalStore<T>(
1248 const isHydrating = getIsHydrating();
1249 if (isHydrating) {
1250 if (getServerSnapshot === undefined) {
1245 - invariant(
1246 - false,
1251 + throw new Error(
1252 'Missing getServerSnapshot, which is required for ' +
1253 'server-rendered content. Will revert to client rendering.',
1254 );
@@ -1279,10 +1284,13 @@ function mountSyncExternalStore<T>(
1284 // the content is stale, it's already visible anyway. Instead we'll patch
1285 // it up in a passive effect.
1286 const root: FiberRoot | null = getWorkInProgressRoot();
1282 - invariant(
1283 - root !== null,
1284 - 'Expected a work-in-progress root. This is a bug in React. Please file an issue.',
1285 - );
1287 +
1288 + if (root === null) {
1289 + throw new Error(
1290 + 'Expected a work-in-progress root. This is a bug in React. Please file an issue.',
1291 + );
1292 + }
1293 +
1294 if (!includesBlockingLane(root, renderLanes)) {
1295 pushStoreConsistencyCheck(fiber, getSnapshot, nextSnapshot);
1296 }
@@ -1376,10 +1384,13 @@ function updateSyncExternalStore<T>(
1384 // Right before committing, we will walk the tree and check if any of the
1385 // stores were mutated.
1386 const root: FiberRoot | null = getWorkInProgressRoot();
1379 - invariant(
1380 - root !== null,
1381 - 'Expected a work-in-progress root. This is a bug in React. Please file an issue.',
1382 - );
1387 +
1388 + if (root === null) {
1389 + throw new Error(
1390 + 'Expected a work-in-progress root. This is a bug in React. Please file an issue.',
1391 + );
1392 + }
1393 +
1394 if (!includesBlockingLane(root, renderLanes)) {
1395 pushStoreConsistencyCheck(fiber, getSnapshot, nextSnapshot);
1396 }
@@ -1532,6 +1543,7 @@ function pushEffect(tag, create, destroy, deps) {
1543 let stackContainsErrorMessage: boolean | null = null;
1544
1545 function getCallerStackFrame(): string {
1546 + // eslint-disable-next-line react-internal/prod-error-codes
1547 const stackFrames = new Error('Error message').stack.split('\n');
1548
1549 // Some browsers (e.g. Chrome) include the error message in the stack
@@ -2051,8 +2063,8 @@ function mountOpaqueIdentifier(): OpaqueIDType | void {
2063 setId(makeId());
2064 }
2065 }
2054 - invariant(
2055 - false,
2066 +
2067 + throw new Error(
2068 'The object passed back from useOpaqueIdentifier is meant to be ' +
2069 'passed through to attributes only. Do not read the value directly.',
2070 );
@@ -2383,7 +2395,7 @@ function markUpdateInDevTools(fiber, lane, action) {
2395
2396 function getCacheForType<T>(resourceType: () => T): T {
2397 if (!enableCache) {
2386 - invariant(false, 'Not implemented.');
2398 + throw new Error('Not implemented.');
2399 }
2400 const cache: Cache = readContext(CacheContext);
2401 let cacheForType: T | void = (cache.get(resourceType): any);
packages/react-reconciler/src/ReactFiberHooks.old.js
+56 -44
@@ -88,7 +88,6 @@ import {
88 isInterleavedUpdate,
89 } from './ReactFiberWorkLoop.old';
90
91 -import invariant from 'shared/invariant';
91 import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
92 import is from 'shared/objectIs';
93 import isArray from 'shared/isArray';
@@ -310,8 +309,7 @@ function warnOnHookMismatchInDev(currentHookName: HookType) {
309 }
310
311 function throwInvalidHookError() {
313 - invariant(
314 - false,
312 + throw new Error(
313 'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for' +
314 ' one of the following reasons:\n' +
315 '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' +
@@ -436,11 +434,13 @@ export function renderWithHooks<Props, SecondArg>(
434 let numberOfReRenders: number = 0;
435 do {
436 didScheduleRenderPhaseUpdateDuringThisPass = false;
439 - invariant(
440 - numberOfReRenders < RE_RENDER_LIMIT,
441 - 'Too many re-renders. React limits the number of renders to prevent ' +
442 - 'an infinite loop.',
443 - );
437 +
438 + if (numberOfReRenders >= RE_RENDER_LIMIT) {
439 + throw new Error(
440 + 'Too many re-renders. React limits the number of renders to prevent ' +
441 + 'an infinite loop.',
442 + );
443 + }
444
445 numberOfReRenders += 1;
446 if (__DEV__) {
@@ -516,11 +516,12 @@ export function renderWithHooks<Props, SecondArg>(
516
517 didScheduleRenderPhaseUpdate = false;
518
519 - invariant(
520 - !didRenderTooFewHooks,
521 - 'Rendered fewer hooks than expected. This may be caused by an accidental ' +
522 - 'early return statement.',
523 - );
519 + if (didRenderTooFewHooks) {
520 + throw new Error(
521 + 'Rendered fewer hooks than expected. This may be caused by an accidental ' +
522 + 'early return statement.',
523 + );
524 + }
525
526 if (enableLazyContextPropagation) {
527 if (current !== null) {
@@ -669,10 +670,10 @@ function updateWorkInProgressHook(): Hook {
670 } else {
671 // Clone from the current hook.
672
672 - invariant(
673 - nextCurrentHook !== null,
674 - 'Rendered more hooks than during the previous render.',
675 - );
673 + if (nextCurrentHook === null) {
674 + throw new Error('Rendered more hooks than during the previous render.');
675 + }
676 +
677 currentHook = nextCurrentHook;
678
679 const newHook: Hook = {
@@ -745,10 +746,12 @@ function updateReducer<S, I, A>(
746 ): [S, Dispatch<A>] {
747 const hook = updateWorkInProgressHook();
748 const queue = hook.queue;
748 - invariant(
749 - queue !== null,
750 - 'Should have a queue. This is likely a bug in React. Please file an issue.',
751 - );
749 +
750 + if (queue === null) {
751 + throw new Error(
752 + 'Should have a queue. This is likely a bug in React. Please file an issue.',
753 + );
754 + }
755
756 queue.lastRenderedReducer = reducer;
757
@@ -900,10 +903,12 @@ function rerenderReducer<S, I, A>(
903 ): [S, Dispatch<A>] {
904 const hook = updateWorkInProgressHook();
905 const queue = hook.queue;
903 - invariant(
904 - queue !== null,
905 - 'Should have a queue. This is likely a bug in React. Please file an issue.',
906 - );
906 +
907 + if (queue === null) {
908 + throw new Error(
909 + 'Should have a queue. This is likely a bug in React. Please file an issue.',
910 + );
911 + }
912
913 queue.lastRenderedReducer = reducer;
914
@@ -1037,8 +1042,7 @@ function readFromUnsubscribedMutableSource<Source, Snapshot>(
1042
1043 // We expect this error not to be thrown during the synchronous retry,
1044 // because we blocked interleaved mutations.
1040 - invariant(
1041 - false,
1045 + throw new Error(
1046 'Cannot read from mutable source during the current render without tearing. This may be a bug in React. Please file an issue.',
1047 );
1048 }
@@ -1051,10 +1055,12 @@ function useMutableSource<Source, Snapshot>(
1055 subscribe: MutableSourceSubscribeFn<Source, Snapshot>,
1056 ): Snapshot {
1057 const root = ((getWorkInProgressRoot(): any): FiberRoot);
1054 - invariant(
1055 - root !== null,
1056 - 'Expected a work-in-progress root. This is a bug in React. Please file an issue.',
1057 - );
1058 +
1059 + if (root === null) {
1060 + throw new Error(
1061 + 'Expected a work-in-progress root. This is a bug in React. Please file an issue.',
1062 + );
1063 + }
1064
1065 const getVersion = source._getVersion;
1066 const version = getVersion(source._source);
@@ -1242,8 +1248,7 @@ function mountSyncExternalStore<T>(
1248 const isHydrating = getIsHydrating();
1249 if (isHydrating) {
1250 if (getServerSnapshot === undefined) {
1245 - invariant(
1246 - false,
1251 + throw new Error(
1252 'Missing getServerSnapshot, which is required for ' +
1253 'server-rendered content. Will revert to client rendering.',
1254 );
@@ -1279,10 +1284,13 @@ function mountSyncExternalStore<T>(
1284 // the content is stale, it's already visible anyway. Instead we'll patch
1285 // it up in a passive effect.
1286 const root: FiberRoot | null = getWorkInProgressRoot();
1282 - invariant(
1283 - root !== null,
1284 - 'Expected a work-in-progress root. This is a bug in React. Please file an issue.',
1285 - );
1287 +
1288 + if (root === null) {
1289 + throw new Error(
1290 + 'Expected a work-in-progress root. This is a bug in React. Please file an issue.',
1291 + );
1292 + }
1293 +
1294 if (!includesBlockingLane(root, renderLanes)) {
1295 pushStoreConsistencyCheck(fiber, getSnapshot, nextSnapshot);
1296 }
@@ -1376,10 +1384,13 @@ function updateSyncExternalStore<T>(
1384 // Right before committing, we will walk the tree and check if any of the
1385 // stores were mutated.
1386 const root: FiberRoot | null = getWorkInProgressRoot();
1379 - invariant(
1380 - root !== null,
1381 - 'Expected a work-in-progress root. This is a bug in React. Please file an issue.',
1382 - );
1387 +
1388 + if (root === null) {
1389 + throw new Error(
1390 + 'Expected a work-in-progress root. This is a bug in React. Please file an issue.',
1391 + );
1392 + }
1393 +
1394 if (!includesBlockingLane(root, renderLanes)) {
1395 pushStoreConsistencyCheck(fiber, getSnapshot, nextSnapshot);
1396 }
@@ -1532,6 +1543,7 @@ function pushEffect(tag, create, destroy, deps) {
1543 let stackContainsErrorMessage: boolean | null = null;
1544
1545 function getCallerStackFrame(): string {
1546 + // eslint-disable-next-line react-internal/prod-error-codes
1547 const stackFrames = new Error('Error message').stack.split('\n');
1548
1549 // Some browsers (e.g. Chrome) include the error message in the stack
@@ -2051,8 +2063,8 @@ function mountOpaqueIdentifier(): OpaqueIDType | void {
2063 setId(makeId());
2064 }
2065 }
2054 - invariant(
2055 - false,
2066 +
2067 + throw new Error(
2068 'The object passed back from useOpaqueIdentifier is meant to be ' +
2069 'passed through to attributes only. Do not read the value directly.',
2070 );
@@ -2383,7 +2395,7 @@ function markUpdateInDevTools(fiber, lane, action) {
2395
2396 function getCacheForType<T>(resourceType: () => T): T {
2397 if (!enableCache) {
2386 - invariant(false, 'Not implemented.');
2398 + throw new Error('Not implemented.');
2399 }
2400 const cache: Cache = readContext(CacheContext);
2401 let cacheForType: T | void = (cache.get(resourceType): any);
packages/react-reconciler/src/ReactFiberHostConfig.js
+2 -4
@@ -7,9 +7,7 @@
7 * @flow
8 */
9
10 -/* eslint-disable react-internal/invariant-args */
11 -
12 -import invariant from 'shared/invariant';
10 +/* eslint-disable react-internal/prod-error-codes */
11
12 // We expect that our Rollup, Jest, and Flow configurations
13 // always shim this module with the corresponding host config
@@ -19,4 +17,4 @@ import invariant from 'shared/invariant';
17 // sure that if we *do* accidentally break the configuration,
18 // the failure isn't silent.
19
22 -invariant(false, 'This module must be shimmed by a specific renderer.');
20 +throw new Error('This module must be shimmed by a specific renderer.');
packages/react-reconciler/src/ReactFiberHostConfigWithNoHydration.js
+1 -4
@@ -7,14 +7,11 @@
7 * @flow
8 */
9
10 -import invariant from 'shared/invariant';
11 -
10 // Renderers that don't support hydration
11 // can re-export everything from this module.
12
13 function shim(...args: any) {
16 - invariant(
17 - false,
14 + throw new Error(
15 'The current renderer does not support hydration. ' +
16 'This error is likely caused by a bug in React. ' +
17 'Please file an issue.',
packages/react-reconciler/src/ReactFiberHostConfigWithNoMicrotasks.js
+1 -4
@@ -7,14 +7,11 @@
7 * @flow
8 */
9
10 -import invariant from 'shared/invariant';
11 -
10 // Renderers that don't support microtasks
11 // can re-export everything from this module.
12
13 function shim(...args: any) {
16 - invariant(
17 - false,
14 + throw new Error(
15 'The current renderer does not support microtasks. ' +
16 'This error is likely caused by a bug in React. ' +
17 'Please file an issue.',
packages/react-reconciler/src/ReactFiberHostConfigWithNoMutation.js
+1 -4
@@ -7,14 +7,11 @@
7 * @flow
8 */
9
10 -import invariant from 'shared/invariant';
11 -
10 // Renderers that don't support mutation
11 // can re-export everything from this module.
12
13 function shim(...args: any) {
16 - invariant(
17 - false,
14 + throw new Error(
15 'The current renderer does not support mutation. ' +
16 'This error is likely caused by a bug in React. ' +
17 'Please file an issue.',
packages/react-reconciler/src/ReactFiberHostConfigWithNoPersistence.js
+1 -4
@@ -7,14 +7,11 @@
7 * @flow
8 */
9
10 -import invariant from 'shared/invariant';
11 -
10 // Renderers that don't support persistence
11 // can re-export everything from this module.
12
13 function shim(...args: any) {
16 - invariant(
17 - false,
14 + throw new Error(
15 'The current renderer does not support persistence. ' +
16 'This error is likely caused by a bug in React. ' +
17 'Please file an issue.',
packages/react-reconciler/src/ReactFiberHostConfigWithNoScopes.js
+1 -4
@@ -7,14 +7,11 @@
7 * @flow
8 */
9
10 -import invariant from 'shared/invariant';
11 -
10 // Renderers that don't support React Scopes
11 // can re-export everything from this module.
12
13 function shim(...args: any) {
16 - invariant(
17 - false,
14 + throw new Error(
15 'The current renderer does not support React Scopes. ' +
16 'This error is likely caused by a bug in React. ' +
17 'Please file an issue.',
packages/react-reconciler/src/ReactFiberHostConfigWithNoTestSelectors.js
+1 -4
@@ -7,14 +7,11 @@
7 * @flow
8 */
9
10 -import invariant from 'shared/invariant';
11 -
10 // Renderers that don't support test selectors
11 // can re-export everything from this module.
12
13 function shim(...args: any) {
16 - invariant(
17 - false,
14 + throw new Error(
15 'The current renderer does not support test selectors. ' +
16 'This error is likely caused by a bug in React. ' +
17 'Please file an issue.',
packages/react-reconciler/src/ReactFiberHostContext.new.js
+7 -7
@@ -11,8 +11,6 @@ import type {Fiber} from './ReactInternalTypes';
11 import type {StackCursor} from './ReactFiberStack.new';
12 import type {Container, HostContext} from './ReactFiberHostConfig';
13
14 -import invariant from 'shared/invariant';
15 -
14 import {getChildHostContext, getRootHostContext} from './ReactFiberHostConfig';
15 import {createCursor, push, pop} from './ReactFiberStack.new';
16
@@ -30,11 +28,13 @@ const rootInstanceStackCursor: StackCursor<
28 > = createCursor(NO_CONTEXT);
29
30 function requiredContext<Value>(c: Value | NoContextT): Value {
33 - invariant(
34 - c !== NO_CONTEXT,
35 - 'Expected host context to exist. This error is likely caused by a bug ' +
36 - 'in React. Please file an issue.',
37 - );
31 + if (c === NO_CONTEXT) {
32 + throw new Error(
33 + 'Expected host context to exist. This error is likely caused by a bug ' +
34 + 'in React. Please file an issue.',
35 + );
36 + }
37 +
38 return (c: any);
39 }
40
packages/react-reconciler/src/ReactFiberHostContext.old.js
+7 -7
@@ -11,8 +11,6 @@ import type {Fiber} from './ReactInternalTypes';
11 import type {StackCursor} from './ReactFiberStack.old';
12 import type {Container, HostContext} from './ReactFiberHostConfig';
13
14 -import invariant from 'shared/invariant';
15 -
14 import {getChildHostContext, getRootHostContext} from './ReactFiberHostConfig';
15 import {createCursor, push, pop} from './ReactFiberStack.old';
16
@@ -30,11 +28,13 @@ const rootInstanceStackCursor: StackCursor<
28 > = createCursor(NO_CONTEXT);
29
30 function requiredContext<Value>(c: Value | NoContextT): Value {
33 - invariant(
34 - c !== NO_CONTEXT,
35 - 'Expected host context to exist. This error is likely caused by a bug ' +
36 - 'in React. Please file an issue.',
37 - );
31 + if (c === NO_CONTEXT) {
32 + throw new Error(
33 + 'Expected host context to exist. This error is likely caused by a bug ' +
34 + 'in React. Please file an issue.',
35 + );
36 + }
37 +
38 return (c: any);
39 }
40
packages/react-reconciler/src/ReactFiberHotReloading.new.js
+2
@@ -7,6 +7,8 @@
7 * @flow
8 */
9
10 +/* eslint-disable react-internal/prod-error-codes */
11 +
12 import type {ReactElement} from 'shared/ReactElementType';
13 import type {Fiber} from './ReactInternalTypes';
14 import type {FiberRoot} from './ReactInternalTypes';
packages/react-reconciler/src/ReactFiberHotReloading.old.js
+2
@@ -7,6 +7,8 @@
7 * @flow
8 */
9
10 +/* eslint-disable react-internal/prod-error-codes */
11 +
12 import type {ReactElement} from 'shared/ReactElementType';
13 import type {Fiber} from './ReactInternalTypes';
14 import type {FiberRoot} from './ReactInternalTypes';
packages/react-reconciler/src/ReactFiberHydrationContext.new.js
+20 -19
@@ -25,7 +25,6 @@ import {
25 SuspenseComponent,
26 } from './ReactWorkTags';
27 import {ChildDeletion, Placement, Hydrating} from './ReactFiberFlags';
28 -import invariant from 'shared/invariant';
28
29 import {
30 createFiberFromHostInstanceForDeletion,
@@ -293,8 +292,7 @@ function prepareToHydrateHostInstance(
292 hostContext: HostContext,
293 ): boolean {
294 if (!supportsHydration) {
296 - invariant(
297 - false,
295 + throw new Error(
296 'Expected prepareToHydrateHostInstance() to never be called. ' +
297 'This error is likely caused by a bug in React. Please file an issue.',
298 );
@@ -321,8 +319,7 @@ function prepareToHydrateHostInstance(
319
320 function prepareToHydrateHostTextInstance(fiber: Fiber): boolean {
321 if (!supportsHydration) {
324 - invariant(
325 - false,
322 + throw new Error(
323 'Expected prepareToHydrateHostTextInstance() to never be called. ' +
324 'This error is likely caused by a bug in React. Please file an issue.',
325 );
@@ -369,8 +366,7 @@ function prepareToHydrateHostTextInstance(fiber: Fiber): boolean {
366
367 function prepareToHydrateHostSuspenseInstance(fiber: Fiber): void {
368 if (!supportsHydration) {
372 - invariant(
373 - false,
369 + throw new Error(
370 'Expected prepareToHydrateHostSuspenseInstance() to never be called. ' +
371 'This error is likely caused by a bug in React. Please file an issue.',
372 );
@@ -379,11 +375,14 @@ function prepareToHydrateHostSuspenseInstance(fiber: Fiber): void {
375 const suspenseState: null | SuspenseState = fiber.memoizedState;
376 const suspenseInstance: null | SuspenseInstance =
377 suspenseState !== null ? suspenseState.dehydrated : null;
382 - invariant(
383 - suspenseInstance,
384 - 'Expected to have a hydrated suspense instance. ' +
385 - 'This error is likely caused by a bug in React. Please file an issue.',
386 - );
378 +
379 + if (!suspenseInstance) {
380 + throw new Error(
381 + 'Expected to have a hydrated suspense instance. ' +
382 + 'This error is likely caused by a bug in React. Please file an issue.',
383 + );
384 + }
385 +
386 hydrateSuspenseInstance(suspenseInstance, fiber);
387 }
388
@@ -391,8 +390,7 @@ function skipPastDehydratedSuspenseInstance(
390 fiber: Fiber,
391 ): null | HydratableInstance {
392 if (!supportsHydration) {
394 - invariant(
395 - false,
393 + throw new Error(
394 'Expected skipPastDehydratedSuspenseInstance() to never be called. ' +
395 'This error is likely caused by a bug in React. Please file an issue.',
396 );
@@ -400,11 +398,14 @@ function skipPastDehydratedSuspenseInstance(
398 const suspenseState: null | SuspenseState = fiber.memoizedState;
399 const suspenseInstance: null | SuspenseInstance =
400 suspenseState !== null ? suspenseState.dehydrated : null;
403 - invariant(
404 - suspenseInstance,
405 - 'Expected to have a hydrated suspense instance. ' +
406 - 'This error is likely caused by a bug in React. Please file an issue.',
407 - );
401 +
402 + if (!suspenseInstance) {
403 + throw new Error(
404 + 'Expected to have a hydrated suspense instance. ' +
405 + 'This error is likely caused by a bug in React. Please file an issue.',
406 + );
407 + }
408 +
409 return getNextHydratableInstanceAfterSuspenseInstance(suspenseInstance);
410 }
411
packages/react-reconciler/src/ReactFiberHydrationContext.old.js
+20 -19
@@ -25,7 +25,6 @@ import {
25 SuspenseComponent,
26 } from './ReactWorkTags';
27 import {ChildDeletion, Placement, Hydrating} from './ReactFiberFlags';
28 -import invariant from 'shared/invariant';
28
29 import {
30 createFiberFromHostInstanceForDeletion,
@@ -293,8 +292,7 @@ function prepareToHydrateHostInstance(
292 hostContext: HostContext,
293 ): boolean {
294 if (!supportsHydration) {
296 - invariant(
297 - false,
295 + throw new Error(
296 'Expected prepareToHydrateHostInstance() to never be called. ' +
297 'This error is likely caused by a bug in React. Please file an issue.',
298 );
@@ -321,8 +319,7 @@ function prepareToHydrateHostInstance(
319
320 function prepareToHydrateHostTextInstance(fiber: Fiber): boolean {
321 if (!supportsHydration) {
324 - invariant(
325 - false,
322 + throw new Error(
323 'Expected prepareToHydrateHostTextInstance() to never be called. ' +
324 'This error is likely caused by a bug in React. Please file an issue.',
325 );
@@ -369,8 +366,7 @@ function prepareToHydrateHostTextInstance(fiber: Fiber): boolean {
366
367 function prepareToHydrateHostSuspenseInstance(fiber: Fiber): void {
368 if (!supportsHydration) {
372 - invariant(
373 - false,
369 + throw new Error(
370 'Expected prepareToHydrateHostSuspenseInstance() to never be called. ' +
371 'This error is likely caused by a bug in React. Please file an issue.',
372 );
@@ -379,11 +375,14 @@ function prepareToHydrateHostSuspenseInstance(fiber: Fiber): void {
375 const suspenseState: null | SuspenseState = fiber.memoizedState;
376 const suspenseInstance: null | SuspenseInstance =
377 suspenseState !== null ? suspenseState.dehydrated : null;
382 - invariant(
383 - suspenseInstance,
384 - 'Expected to have a hydrated suspense instance. ' +
385 - 'This error is likely caused by a bug in React. Please file an issue.',
386 - );
378 +
379 + if (!suspenseInstance) {
380 + throw new Error(
381 + 'Expected to have a hydrated suspense instance. ' +
382 + 'This error is likely caused by a bug in React. Please file an issue.',
383 + );
384 + }
385 +
386 hydrateSuspenseInstance(suspenseInstance, fiber);
387 }
388
@@ -391,8 +390,7 @@ function skipPastDehydratedSuspenseInstance(
390 fiber: Fiber,
391 ): null | HydratableInstance {
392 if (!supportsHydration) {
394 - invariant(
395 - false,
393 + throw new Error(
394 'Expected skipPastDehydratedSuspenseInstance() to never be called. ' +
395 'This error is likely caused by a bug in React. Please file an issue.',
396 );
@@ -400,11 +398,14 @@ function skipPastDehydratedSuspenseInstance(
398 const suspenseState: null | SuspenseState = fiber.memoizedState;
399 const suspenseInstance: null | SuspenseInstance =
400 suspenseState !== null ? suspenseState.dehydrated : null;
403 - invariant(
404 - suspenseInstance,
405 - 'Expected to have a hydrated suspense instance. ' +
406 - 'This error is likely caused by a bug in React. Please file an issue.',
407 - );
401 +
402 + if (!suspenseInstance) {
403 + throw new Error(
404 + 'Expected to have a hydrated suspense instance. ' +
405 + 'This error is likely caused by a bug in React. Please file an issue.',
406 + );
407 + }
408 +
409 return getNextHydratableInstanceAfterSuspenseInstance(suspenseInstance);
410 }
411
packages/react-reconciler/src/ReactFiberNewContext.new.js
+27 -20
@@ -38,7 +38,6 @@ import {
38 NeedsPropagation,
39 } from './ReactFiberFlags';
40
41 -import invariant from 'shared/invariant';
41 import is from 'shared/objectIs';
42 import {createUpdate, ForceUpdate} from './ReactUpdateQueue.new';
43 import {markWorkInProgressReceivedUpdate} from './ReactFiberBeginWork.new';
@@ -269,10 +268,13 @@ function propagateContextChange_eager<T>(
268 // if it will have any context consumers in it. The best we can do is
269 // mark it as having updates.
270 const parentSuspense = fiber.return;
272 - invariant(
273 - parentSuspense !== null,
274 - 'We just came from a parent so we must have had a parent. This is a bug in React.',
275 - );
271 +
272 + if (parentSuspense === null) {
273 + throw new Error(
274 + 'We just came from a parent so we must have had a parent. This is a bug in React.',
275 + );
276 + }
277 +
278 parentSuspense.lanes = mergeLanes(parentSuspense.lanes, renderLanes);
279 const alternate = parentSuspense.alternate;
280 if (alternate !== null) {
@@ -388,10 +390,13 @@ function propagateContextChanges<T>(
390 // if it will have any context consumers in it. The best we can do is
391 // mark it as having updates.
392 const parentSuspense = fiber.return;
391 - invariant(
392 - parentSuspense !== null,
393 - 'We just came from a parent so we must have had a parent. This is a bug in React.',
394 - );
393 +
394 + if (parentSuspense === null) {
395 + throw new Error(
396 + 'We just came from a parent so we must have had a parent. This is a bug in React.',
397 + );
398 + }
399 +
400 parentSuspense.lanes = mergeLanes(parentSuspense.lanes, renderLanes);
401 const alternate = parentSuspense.alternate;
402 if (alternate !== null) {
@@ -493,10 +498,11 @@ function propagateParentContextChanges(
498
499 if (parent.tag === ContextProvider) {
500 const currentParent = parent.alternate;
496 - invariant(
497 - currentParent !== null,
498 - 'Should have a current fiber. This is a bug in React.',
499 - );
501 +
502 + if (currentParent === null) {
503 + throw new Error('Should have a current fiber. This is a bug in React.');
504 + }
505 +
506 const oldProps = currentParent.memoizedProps;
507 if (oldProps !== null) {
508 const providerType: ReactProviderType<any> = parent.type;
@@ -631,13 +637,14 @@ export function readContext<T>(context: ReactContext<T>): T {
637 };
638
639 if (lastContextDependency === null) {
634 - invariant(
635 - currentlyRenderingFiber !== null,
636 - 'Context can only be read while React is rendering. ' +
637 - 'In classes, you can read it in the render method or getDerivedStateFromProps. ' +
638 - 'In function components, you can read it directly in the function body, but not ' +
639 - 'inside Hooks like useReducer() or useMemo().',
640 - );
640 + if (currentlyRenderingFiber === null) {
641 + throw new Error(
642 + 'Context can only be read while React is rendering. ' +
643 + 'In classes, you can read it in the render method or getDerivedStateFromProps. ' +
644 + 'In function components, you can read it directly in the function body, but not ' +
645 + 'inside Hooks like useReducer() or useMemo().',
646 + );
647 + }
648
649 // This is the first dependency for this component. Create a new list.
650 lastContextDependency = contextItem;
packages/react-reconciler/src/ReactFiberNewContext.old.js
+27 -20
@@ -38,7 +38,6 @@ import {
38 NeedsPropagation,
39 } from './ReactFiberFlags';
40
41 -import invariant from 'shared/invariant';
41 import is from 'shared/objectIs';
42 import {createUpdate, ForceUpdate} from './ReactUpdateQueue.old';
43 import {markWorkInProgressReceivedUpdate} from './ReactFiberBeginWork.old';
@@ -269,10 +268,13 @@ function propagateContextChange_eager<T>(
268 // if it will have any context consumers in it. The best we can do is
269 // mark it as having updates.
270 const parentSuspense = fiber.return;
272 - invariant(
273 - parentSuspense !== null,
274 - 'We just came from a parent so we must have had a parent. This is a bug in React.',
275 - );
271 +
272 + if (parentSuspense === null) {
273 + throw new Error(
274 + 'We just came from a parent so we must have had a parent. This is a bug in React.',
275 + );
276 + }
277 +
278 parentSuspense.lanes = mergeLanes(parentSuspense.lanes, renderLanes);
279 const alternate = parentSuspense.alternate;
280 if (alternate !== null) {
@@ -388,10 +390,13 @@ function propagateContextChanges<T>(
390 // if it will have any context consumers in it. The best we can do is
391 // mark it as having updates.
392 const parentSuspense = fiber.return;
391 - invariant(
392 - parentSuspense !== null,
393 - 'We just came from a parent so we must have had a parent. This is a bug in React.',
394 - );
393 +
394 + if (parentSuspense === null) {
395 + throw new Error(
396 + 'We just came from a parent so we must have had a parent. This is a bug in React.',
397 + );
398 + }
399 +
400 parentSuspense.lanes = mergeLanes(parentSuspense.lanes, renderLanes);
401 const alternate = parentSuspense.alternate;
402 if (alternate !== null) {
@@ -493,10 +498,11 @@ function propagateParentContextChanges(
498
499 if (parent.tag === ContextProvider) {
500 const currentParent = parent.alternate;
496 - invariant(
497 - currentParent !== null,
498 - 'Should have a current fiber. This is a bug in React.',
499 - );
501 +
502 + if (currentParent === null) {
503 + throw new Error('Should have a current fiber. This is a bug in React.');
504 + }
505 +
506 const oldProps = currentParent.memoizedProps;
507 if (oldProps !== null) {
508 const providerType: ReactProviderType<any> = parent.type;
@@ -631,13 +637,14 @@ export function readContext<T>(context: ReactContext<T>): T {
637 };
638
639 if (lastContextDependency === null) {
634 - invariant(
635 - currentlyRenderingFiber !== null,
636 - 'Context can only be read while React is rendering. ' +
637 - 'In classes, you can read it in the render method or getDerivedStateFromProps. ' +
638 - 'In function components, you can read it directly in the function body, but not ' +
639 - 'inside Hooks like useReducer() or useMemo().',
640 - );
640 + if (currentlyRenderingFiber === null) {
641 + throw new Error(
642 + 'Context can only be read while React is rendering. ' +
643 + 'In classes, you can read it in the render method or getDerivedStateFromProps. ' +
644 + 'In function components, you can read it directly in the function body, but not ' +
645 + 'inside Hooks like useReducer() or useMemo().',
646 + );
647 + }
648
649 // This is the first dependency for this component. Create a new list.
650 lastContextDependency = contextItem;
packages/react-reconciler/src/ReactFiberReconciler.new.js
+8 -11
@@ -33,7 +33,6 @@ import {
33 SuspenseComponent,
34 } from './ReactWorkTags';
35 import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
36 -import invariant from 'shared/invariant';
36 import isArray from 'shared/isArray';
37 import {enableSchedulingProfiler} from 'shared/ReactFeatureFlags';
38 import ReactSharedInternals from 'shared/ReactSharedInternals';
@@ -153,12 +152,11 @@ function findHostInstance(component: Object): PublicInstance | null {
152 const fiber = getInstance(component);
153 if (fiber === undefined) {
154 if (typeof component.render === 'function') {
156 - invariant(false, 'Unable to find node on an unmounted component.');
155 + throw new Error('Unable to find node on an unmounted component.');
156 } else {
158 - invariant(
159 - false,
160 - 'Argument appears to not be a ReactComponent. Keys: %s',
161 - Object.keys(component),
157 + const keys = Object.keys(component).join(',');
158 + throw new Error(
159 + `Argument appears to not be a ReactComponent. Keys: ${keys}`,
160 );
161 }
162 }
@@ -177,12 +175,11 @@ function findHostInstanceWithWarning(
175 const fiber = getInstance(component);
176 if (fiber === undefined) {
177 if (typeof component.render === 'function') {
180 - invariant(false, 'Unable to find node on an unmounted component.');
178 + throw new Error('Unable to find node on an unmounted component.');
179 } else {
182 - invariant(
183 - false,
184 - 'Argument appears to not be a ReactComponent. Keys: %s',
185 - Object.keys(component),
180 + const keys = Object.keys(component).join(',');
181 + throw new Error(
182 + `Argument appears to not be a ReactComponent. Keys: ${keys}`,
183 );
184 }
185 }
packages/react-reconciler/src/ReactFiberReconciler.old.js
+8 -11
@@ -33,7 +33,6 @@ import {
33 SuspenseComponent,
34 } from './ReactWorkTags';
35 import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
36 -import invariant from 'shared/invariant';
36 import isArray from 'shared/isArray';
37 import {enableSchedulingProfiler} from 'shared/ReactFeatureFlags';
38 import ReactSharedInternals from 'shared/ReactSharedInternals';
@@ -153,12 +152,11 @@ function findHostInstance(component: Object): PublicInstance | null {
152 const fiber = getInstance(component);
153 if (fiber === undefined) {
154 if (typeof component.render === 'function') {
156 - invariant(false, 'Unable to find node on an unmounted component.');
155 + throw new Error('Unable to find node on an unmounted component.');
156 } else {
158 - invariant(
159 - false,
160 - 'Argument appears to not be a ReactComponent. Keys: %s',
161 - Object.keys(component),
157 + const keys = Object.keys(component).join(',');
158 + throw new Error(
159 + `Argument appears to not be a ReactComponent. Keys: ${keys}`,
160 );
161 }
162 }
@@ -177,12 +175,11 @@ function findHostInstanceWithWarning(
175 const fiber = getInstance(component);
176 if (fiber === undefined) {
177 if (typeof component.render === 'function') {
180 - invariant(false, 'Unable to find node on an unmounted component.');
178 + throw new Error('Unable to find node on an unmounted component.');
179 } else {
182 - invariant(
183 - false,
184 - 'Argument appears to not be a ReactComponent. Keys: %s',
185 - Object.keys(component),
180 + const keys = Object.keys(component).join(',');
181 + throw new Error(
182 + `Argument appears to not be a ReactComponent. Keys: ${keys}`,
183 );
184 }
185 }
packages/react-reconciler/src/ReactFiberThrow.new.js
+2 -1
@@ -474,7 +474,8 @@ function throwException(
474 return;
475 } else {
476 // No boundary was found. Fallthrough to error mode.
477 - // TODO: Use invariant so the message is stripped in prod?
477 + // TODO: We should never call getComponentNameFromFiber in production.
478 + // Log a warning or something to prevent us from accidentally bundling it.
479 value = new Error(
480 (getComponentNameFromFiber(sourceFiber) || 'A React component') +
481 ' suspended while rendering, but no fallback UI was specified.\n' +
packages/react-reconciler/src/ReactFiberThrow.old.js
+2 -1
@@ -474,7 +474,8 @@ function throwException(
474 return;
475 } else {
476 // No boundary was found. Fallthrough to error mode.
477 - // TODO: Use invariant so the message is stripped in prod?
477 + // TODO: We should never call getComponentNameFromFiber in production.
478 + // Log a warning or something to prevent us from accidentally bundling it.
479 value = new Error(
480 (getComponentNameFromFiber(sourceFiber) || 'A React component') +
481 ' suspended while rendering, but no fallback UI was specified.\n' +
packages/react-reconciler/src/ReactFiberTreeReflection.js
+28 -25
@@ -11,8 +11,6 @@ import type {Fiber} from './ReactInternalTypes';
11 import type {Container, SuspenseInstance} from './ReactFiberHostConfig';
12 import type {SuspenseState} from './ReactFiberSuspenseComponent.old';
13
14 -import invariant from 'shared/invariant';
15 -
14 import {get as getInstance} from 'shared/ReactInstanceMap';
15 import ReactSharedInternals from 'shared/ReactSharedInternals';
16 import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
@@ -116,10 +114,9 @@ export function isMounted(component: React$Component<any, any>): boolean {
114 }
115
116 function assertIsMounted(fiber) {
119 - invariant(
120 - getNearestMountedFiber(fiber) === fiber,
121 - 'Unable to find node on an unmounted component.',
122 - );
117 + if (getNearestMountedFiber(fiber) !== fiber) {
118 + throw new Error('Unable to find node on an unmounted component.');
119 + }
120 }
121
122 export function findCurrentFiberUsingSlowPath(fiber: Fiber): Fiber | null {
@@ -127,10 +124,11 @@ export function findCurrentFiberUsingSlowPath(fiber: Fiber): Fiber | null {
124 if (!alternate) {
125 // If there is no alternate, then we only need to check if it is mounted.
126 const nearestMounted = getNearestMountedFiber(fiber);
130 - invariant(
131 - nearestMounted !== null,
132 - 'Unable to find node on an unmounted component.',
133 - );
127 +
128 + if (nearestMounted === null) {
129 + throw new Error('Unable to find node on an unmounted component.');
130 + }
131 +
132 if (nearestMounted !== fiber) {
133 return null;
134 }
@@ -180,9 +178,10 @@ export function findCurrentFiberUsingSlowPath(fiber: Fiber): Fiber | null {
178 }
179 child = child.sibling;
180 }
181 +
182 // We should never have an alternate for any mounting node. So the only
183 // way this could possibly happen is if this was unmounted, if at all.
185 - invariant(false, 'Unable to find node on an unmounted component.');
184 + throw new Error('Unable to find node on an unmounted component.');
185 }
186
187 if (a.return !== b.return) {
@@ -233,26 +232,30 @@ export function findCurrentFiberUsingSlowPath(fiber: Fiber): Fiber | null {
232 }
233 child = child.sibling;
234 }
236 - invariant(
237 - didFindChild,
238 - 'Child was not found in either parent set. This indicates a bug ' +
239 - 'in React related to the return pointer. Please file an issue.',
240 - );
235 +
236 + if (!didFindChild) {
237 + throw new Error(
238 + 'Child was not found in either parent set. This indicates a bug ' +
239 + 'in React related to the return pointer. Please file an issue.',
240 + );
241 + }
242 }
243 }
244
244 - invariant(
245 - a.alternate === b,
246 - "Return fibers should always be each others' alternates. " +
247 - 'This error is likely caused by a bug in React. Please file an issue.',
248 - );
245 + if (a.alternate !== b) {
246 + throw new Error(
247 + "Return fibers should always be each others' alternates. " +
248 + 'This error is likely caused by a bug in React. Please file an issue.',
249 + );
250 + }
251 }
252 +
253 // If the root is not a host container, we're in a disconnected tree. I.e.
254 // unmounted.
252 - invariant(
253 - a.tag === HostRoot,
254 - 'Unable to find node on an unmounted component.',
255 - );
255 + if (a.tag !== HostRoot) {
256 + throw new Error('Unable to find node on an unmounted component.');
257 + }
258 +
259 if (a.stateNode.current === a) {
260 // We've determined that A is the current branch.
261 return fiber;
packages/react-reconciler/src/ReactFiberUnwindWork.new.js
+15 -12
@@ -51,8 +51,6 @@ import {
51 } from './ReactFiberCacheComponent.new';
52 import {transferActualDuration} from './ReactProfilerTimer.new';
53
54 -import invariant from 'shared/invariant';
55 -
54 function unwindWork(workInProgress: Fiber, renderLanes: Lanes) {
55 switch (workInProgress.tag) {
56 case ClassComponent: {
@@ -85,11 +83,14 @@ function unwindWork(workInProgress: Fiber, renderLanes: Lanes) {
83 popTopLevelLegacyContextObject(workInProgress);
84 resetMutableSourceWorkInProgressVersions();
85 const flags = workInProgress.flags;
88 - invariant(
89 - (flags & DidCapture) === NoFlags,
90 - 'The root failed to unmount after an error. This is likely a bug in ' +
91 - 'React. Please file an issue.',
92 - );
86 +
87 + if ((flags & DidCapture) !== NoFlags) {
88 + throw new Error(
89 + 'The root failed to unmount after an error. This is likely a bug in ' +
90 + 'React. Please file an issue.',
91 + );
92 + }
93 +
94 workInProgress.flags = (flags & ~ShouldCapture) | DidCapture;
95 return workInProgress;
96 }
@@ -104,11 +105,13 @@ function unwindWork(workInProgress: Fiber, renderLanes: Lanes) {
105 const suspenseState: null | SuspenseState =
106 workInProgress.memoizedState;
107 if (suspenseState !== null && suspenseState.dehydrated !== null) {
107 - invariant(
108 - workInProgress.alternate !== null,
109 - 'Threw in newly mounted dehydrated component. This is likely a bug in ' +
110 - 'React. Please file an issue.',
111 - );
108 + if (workInProgress.alternate === null) {
109 + throw new Error(
110 + 'Threw in newly mounted dehydrated component. This is likely a bug in ' +
111 + 'React. Please file an issue.',
112 + );
113 + }
114 +
115 resetHydrationState();
116 }
117 }
packages/react-reconciler/src/ReactFiberUnwindWork.old.js
+15 -12
@@ -51,8 +51,6 @@ import {
51 } from './ReactFiberCacheComponent.old';
52 import {transferActualDuration} from './ReactProfilerTimer.old';
53
54 -import invariant from 'shared/invariant';
55 -
54 function unwindWork(workInProgress: Fiber, renderLanes: Lanes) {
55 switch (workInProgress.tag) {
56 case ClassComponent: {
@@ -85,11 +83,14 @@ function unwindWork(workInProgress: Fiber, renderLanes: Lanes) {
83 popTopLevelLegacyContextObject(workInProgress);
84 resetMutableSourceWorkInProgressVersions();
85 const flags = workInProgress.flags;
88 - invariant(
89 - (flags & DidCapture) === NoFlags,
90 - 'The root failed to unmount after an error. This is likely a bug in ' +
91 - 'React. Please file an issue.',
92 - );
86 +
87 + if ((flags & DidCapture) !== NoFlags) {
88 + throw new Error(
89 + 'The root failed to unmount after an error. This is likely a bug in ' +
90 + 'React. Please file an issue.',
91 + );
92 + }
93 +
94 workInProgress.flags = (flags & ~ShouldCapture) | DidCapture;
95 return workInProgress;
96 }
@@ -104,11 +105,13 @@ function unwindWork(workInProgress: Fiber, renderLanes: Lanes) {
105 const suspenseState: null | SuspenseState =
106 workInProgress.memoizedState;
107 if (suspenseState !== null && suspenseState.dehydrated !== null) {
107 - invariant(
108 - workInProgress.alternate !== null,
109 - 'Threw in newly mounted dehydrated component. This is likely a bug in ' +
110 - 'React. Please file an issue.',
111 - );
108 + if (workInProgress.alternate === null) {
109 + throw new Error(
110 + 'Threw in newly mounted dehydrated component. This is likely a bug in ' +
111 + 'React. Please file an issue.',
112 + );
113 + }
114 +
115 resetHydrationState();
116 }
117 }
packages/react-reconciler/src/ReactFiberWorkLoop.new.js
+24 -30
@@ -34,7 +34,6 @@ import {
34 warnOnSubscriptionInsideStartTransition,
35 } from 'shared/ReactFeatureFlags';
36 import ReactSharedInternals from 'shared/ReactSharedInternals';
37 -import invariant from 'shared/invariant';
37 import is from 'shared/objectIs';
38
39 import {
@@ -737,10 +736,9 @@ function performConcurrentWorkOnRoot(root, didTimeout) {
736 currentEventTime = NoTimestamp;
737 currentEventTransitionLane = NoLanes;
738
740 - invariant(
741 - (executionContext & (RenderContext | CommitContext)) === NoContext,
742 - 'Should not already be working.',
743 - );
739 + if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
740 + throw new Error('Should not already be working.');
741 + }
742
743 // Flush any pending passive effects before deciding which lanes to work on,
744 // in case they schedule additional work.
@@ -878,7 +876,7 @@ function finishConcurrentRender(root, exitStatus, lanes) {
876 switch (exitStatus) {
877 case RootIncomplete:
878 case RootFatalErrored: {
881 - invariant(false, 'Root did not complete. This is a bug in React.');
879 + throw new Error('Root did not complete. This is a bug in React.');
880 }
881 // Flow knows about invariant, so it complains if I add a break
882 // statement, but eslint doesn't know about invariant, so it complains
@@ -981,7 +979,7 @@ function finishConcurrentRender(root, exitStatus, lanes) {
979 break;
980 }
981 default: {
984 - invariant(false, 'Unknown root exit status.');
982 + throw new Error('Unknown root exit status.');
983 }
984 }
985 }
@@ -1055,10 +1053,9 @@ function performSyncWorkOnRoot(root) {
1053 syncNestedUpdateFlag();
1054 }
1055
1058 - invariant(
1059 - (executionContext & (RenderContext | CommitContext)) === NoContext,
1060 - 'Should not already be working.',
1061 - );
1056 + if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
1057 + throw new Error('Should not already be working.');
1058 + }
1059
1060 flushPassiveEffects();
1061
@@ -1525,8 +1522,7 @@ function renderRootSync(root: FiberRoot, lanes: Lanes) {
1522
1523 if (workInProgress !== null) {
1524 // This is a sync render, so we should have finished the whole tree.
1528 - invariant(
1529 - false,
1525 + throw new Error(
1526 'Cannot commit an incomplete root. This error is likely caused by a ' +
1527 'bug in React. Please file an issue.',
1528 );
@@ -1796,10 +1792,9 @@ function commitRootImpl(root, renderPriorityLevel) {
1792 } while (rootWithPendingPassiveEffects !== null);
1793 flushRenderPhaseStrictModeWarningsInDEV();
1794
1799 - invariant(
1800 - (executionContext & (RenderContext | CommitContext)) === NoContext,
1801 - 'Should not already be working.',
1802 - );
1795 + if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
1796 + throw new Error('Should not already be working.');
1797 + }
1798
1799 const finishedWork = root.finishedWork;
1800 const lanes = root.finishedLanes;
@@ -1839,11 +1834,12 @@ function commitRootImpl(root, renderPriorityLevel) {
1834 root.finishedWork = null;
1835 root.finishedLanes = NoLanes;
1836
1842 - invariant(
1843 - finishedWork !== root.current,
1844 - 'Cannot commit the same tree as before. This error is likely caused by ' +
1845 - 'a bug in React. Please file an issue.',
1846 - );
1837 + if (finishedWork === root.current) {
1838 + throw new Error(
1839 + 'Cannot commit the same tree as before. This error is likely caused by ' +
1840 + 'a bug in React. Please file an issue.',
1841 + );
1842 + }
1843
1844 // commitRoot never returns a continuation; it always finishes synchronously.
1845 // So we can clear these now to allow a new callback to be scheduled.
@@ -2154,10 +2150,9 @@ function flushPassiveEffectsImpl() {
2150 // because it's only used for profiling), but it's a refactor hazard.
2151 pendingPassiveEffectsLanes = NoLanes;
2152
2157 - invariant(
2158 - (executionContext & (RenderContext | CommitContext)) === NoContext,
2159 - 'Cannot flush passive effects while already rendering.',
2160 - );
2153 + if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
2154 + throw new Error('Cannot flush passive effects while already rendering.');
2155 + }
2156
2157 if (__DEV__) {
2158 if (enableDebugTracing) {
@@ -2418,8 +2413,7 @@ export function resolveRetryWakeable(boundaryFiber: Fiber, wakeable: Wakeable) {
2413 retryCache = boundaryFiber.stateNode;
2414 break;
2415 default:
2421 - invariant(
2422 - false,
2416 + throw new Error(
2417 'Pinged unknown suspense boundary type. ' +
2418 'This is probably a bug in React.',
2419 );
@@ -2466,8 +2460,8 @@ function checkForNestedUpdates() {
2460 if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {
2461 nestedUpdateCount = 0;
2462 rootWithNestedUpdates = null;
2469 - invariant(
2470 - false,
2463 +
2464 + throw new Error(
2465 'Maximum update depth exceeded. This can happen when a component ' +
2466 'repeatedly calls setState inside componentWillUpdate or ' +
2467 'componentDidUpdate. React limits the number of nested updates to ' +
packages/react-reconciler/src/ReactFiberWorkLoop.old.js
+24 -30
@@ -34,7 +34,6 @@ import {
34 warnOnSubscriptionInsideStartTransition,
35 } from 'shared/ReactFeatureFlags';
36 import ReactSharedInternals from 'shared/ReactSharedInternals';
37 -import invariant from 'shared/invariant';
37 import is from 'shared/objectIs';
38
39 import {
@@ -737,10 +736,9 @@ function performConcurrentWorkOnRoot(root, didTimeout) {
736 currentEventTime = NoTimestamp;
737 currentEventTransitionLane = NoLanes;
738
740 - invariant(
741 - (executionContext & (RenderContext | CommitContext)) === NoContext,
742 - 'Should not already be working.',
743 - );
739 + if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
740 + throw new Error('Should not already be working.');
741 + }
742
743 // Flush any pending passive effects before deciding which lanes to work on,
744 // in case they schedule additional work.
@@ -878,7 +876,7 @@ function finishConcurrentRender(root, exitStatus, lanes) {
876 switch (exitStatus) {
877 case RootIncomplete:
878 case RootFatalErrored: {
881 - invariant(false, 'Root did not complete. This is a bug in React.');
879 + throw new Error('Root did not complete. This is a bug in React.');
880 }
881 // Flow knows about invariant, so it complains if I add a break
882 // statement, but eslint doesn't know about invariant, so it complains
@@ -981,7 +979,7 @@ function finishConcurrentRender(root, exitStatus, lanes) {
979 break;
980 }
981 default: {
984 - invariant(false, 'Unknown root exit status.');
982 + throw new Error('Unknown root exit status.');
983 }
984 }
985 }
@@ -1055,10 +1053,9 @@ function performSyncWorkOnRoot(root) {
1053 syncNestedUpdateFlag();
1054 }
1055
1058 - invariant(
1059 - (executionContext & (RenderContext | CommitContext)) === NoContext,
1060 - 'Should not already be working.',
1061 - );
1056 + if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
1057 + throw new Error('Should not already be working.');
1058 + }
1059
1060 flushPassiveEffects();
1061
@@ -1525,8 +1522,7 @@ function renderRootSync(root: FiberRoot, lanes: Lanes) {
1522
1523 if (workInProgress !== null) {
1524 // This is a sync render, so we should have finished the whole tree.
1528 - invariant(
1529 - false,
1525 + throw new Error(
1526 'Cannot commit an incomplete root. This error is likely caused by a ' +
1527 'bug in React. Please file an issue.',
1528 );
@@ -1796,10 +1792,9 @@ function commitRootImpl(root, renderPriorityLevel) {
1792 } while (rootWithPendingPassiveEffects !== null);
1793 flushRenderPhaseStrictModeWarningsInDEV();
1794
1799 - invariant(
1800 - (executionContext & (RenderContext | CommitContext)) === NoContext,
1801 - 'Should not already be working.',
1802 - );
1795 + if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
1796 + throw new Error('Should not already be working.');
1797 + }
1798
1799 const finishedWork = root.finishedWork;
1800 const lanes = root.finishedLanes;
@@ -1839,11 +1834,12 @@ function commitRootImpl(root, renderPriorityLevel) {
1834 root.finishedWork = null;
1835 root.finishedLanes = NoLanes;
1836
1842 - invariant(
1843 - finishedWork !== root.current,
1844 - 'Cannot commit the same tree as before. This error is likely caused by ' +
1845 - 'a bug in React. Please file an issue.',
1846 - );
1837 + if (finishedWork === root.current) {
1838 + throw new Error(
1839 + 'Cannot commit the same tree as before. This error is likely caused by ' +
1840 + 'a bug in React. Please file an issue.',
1841 + );
1842 + }
1843
1844 // commitRoot never returns a continuation; it always finishes synchronously.
1845 // So we can clear these now to allow a new callback to be scheduled.
@@ -2154,10 +2150,9 @@ function flushPassiveEffectsImpl() {
2150 // because it's only used for profiling), but it's a refactor hazard.
2151 pendingPassiveEffectsLanes = NoLanes;
2152
2157 - invariant(
2158 - (executionContext & (RenderContext | CommitContext)) === NoContext,
2159 - 'Cannot flush passive effects while already rendering.',
2160 - );
2153 + if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
2154 + throw new Error('Cannot flush passive effects while already rendering.');
2155 + }
2156
2157 if (__DEV__) {
2158 if (enableDebugTracing) {
@@ -2418,8 +2413,7 @@ export function resolveRetryWakeable(boundaryFiber: Fiber, wakeable: Wakeable) {
2413 retryCache = boundaryFiber.stateNode;
2414 break;
2415 default:
2421 - invariant(
2422 - false,
2416 + throw new Error(
2417 'Pinged unknown suspense boundary type. ' +
2418 'This is probably a bug in React.',
2419 );
@@ -2466,8 +2460,8 @@ function checkForNestedUpdates() {
2460 if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {
2461 nestedUpdateCount = 0;
2462 rootWithNestedUpdates = null;
2469 - invariant(
2470 - false,
2463 +
2464 + throw new Error(
2465 'Maximum update depth exceeded. This can happen when a component ' +
2466 'repeatedly calls setState inside componentWillUpdate or ' +
2467 'componentDidUpdate. React limits the number of nested updates to ' +
packages/react-reconciler/src/ReactTestSelectors.js
+20 -20
@@ -10,7 +10,6 @@
10 import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
11 import type {Instance} from './ReactFiberHostConfig';
12
13 -import invariant from 'shared/invariant';
13 import {HostComponent, HostText} from 'react-reconciler/src/ReactWorkTags';
14 import getComponentNameFromType from 'shared/getComponentNameFromType';
15 import {
@@ -116,17 +115,22 @@ export function createTestNameSelector(id: string): TestNameSelector {
115 function findFiberRootForHostRoot(hostRoot: Instance): Fiber {
116 const maybeFiber = getInstanceFromNode((hostRoot: any));
117 if (maybeFiber != null) {
119 - invariant(
120 - typeof maybeFiber.memoizedProps['data-testname'] === 'string',
121 - 'Invalid host root specified. Should be either a React container or a node with a testname attribute.',
122 - );
118 + if (typeof maybeFiber.memoizedProps['data-testname'] !== 'string') {
119 + throw new Error(
120 + 'Invalid host root specified. Should be either a React container or a node with a testname attribute.',
121 + );
122 + }
123 +
124 return ((maybeFiber: any): Fiber);
125 } else {
126 const fiberRoot = findFiberRoot(hostRoot);
126 - invariant(
127 - fiberRoot !== null,
128 - 'Could not find React container within specified host subtree.',
129 - );
127 +
128 + if (fiberRoot === null) {
129 + throw new Error(
130 + 'Could not find React container within specified host subtree.',
131 + );
132 + }
133 +
134 // The Flow type for FiberRoot is a little funky.
135 // createFiberRoot() cheats this by treating the root as :any and adding stateNode lazily.
136 return ((fiberRoot: any).stateNode.current: Fiber);
@@ -179,8 +183,7 @@ function matchSelector(fiber: Fiber, selector: Selector): boolean {
183 }
184 break;
185 default:
182 - invariant(null, 'Invalid selector type %s specified.', selector);
183 - break;
186 + throw new Error('Invalid selector type specified.');
187 }
188
189 return false;
@@ -200,11 +203,8 @@ function selectorToString(selector: Selector): string | null {
203 case TEST_NAME_TYPE:
204 return `[data-testname="${((selector: any): TestNameSelector).value}"]`;
205 default:
203 - invariant(null, 'Invalid selector type %s specified.', selector);
204 - break;
206 + throw new Error('Invalid selector type specified.');
207 }
206 -
207 - return null;
208 }
209
210 function findPaths(root: Fiber, selectors: Array<Selector>): Array<Fiber> {
@@ -277,7 +277,7 @@ export function findAllNodes(
277 selectors: Array<Selector>,
278 ): Array<Instance> {
279 if (!supportsTestSelectors) {
280 - invariant(false, 'Test selector API is not supported by this renderer.');
280 + throw new Error('Test selector API is not supported by this renderer.');
281 }
282
283 const root = findFiberRootForHostRoot(hostRoot);
@@ -311,7 +311,7 @@ export function getFindAllNodesFailureDescription(
311 selectors: Array<Selector>,
312 ): string | null {
313 if (!supportsTestSelectors) {
314 - invariant(false, 'Test selector API is not supported by this renderer.');
314 + throw new Error('Test selector API is not supported by this renderer.');
315 }
316
317 const root = findFiberRootForHostRoot(hostRoot);
@@ -376,7 +376,7 @@ export function findBoundingRects(
376 selectors: Array<Selector>,
377 ): Array<BoundingRect> {
378 if (!supportsTestSelectors) {
379 - invariant(false, 'Test selector API is not supported by this renderer.');
379 + throw new Error('Test selector API is not supported by this renderer.');
380 }
381
382 const instanceRoots = findAllNodes(hostRoot, selectors);
@@ -466,7 +466,7 @@ export function focusWithin(
466 selectors: Array<Selector>,
467 ): boolean {
468 if (!supportsTestSelectors) {
469 - invariant(false, 'Test selector API is not supported by this renderer.');
469 + throw new Error('Test selector API is not supported by this renderer.');
470 }
471
472 const root = findFiberRootForHostRoot(hostRoot);
@@ -516,7 +516,7 @@ export function observeVisibleRects(
516 options?: IntersectionObserverOptions,
517 ): {|disconnect: () => void|} {
518 if (!supportsTestSelectors) {
519 - invariant(false, 'Test selector API is not supported by this renderer.');
519 + throw new Error('Test selector API is not supported by this renderer.');
520 }
521
522 const instanceRoots = findAllNodes(hostRoot, selectors);
packages/react-reconciler/src/ReactUpdateQueue.new.js
+7 -8
@@ -112,8 +112,6 @@ import {
112 import {pushInterleavedQueue} from './ReactFiberInterleavedUpdates.new';
113 import {setIsStrictModeForDevtools} from './ReactFiberDevToolsHook.new';
114
115 -import invariant from 'shared/invariant';
116 -
115 export type Update<State> = {|
116 // TODO: Temporary field. Will remove this by storing a map of
117 // transition -> event time on the root.
@@ -656,12 +654,13 @@ export function processUpdateQueue<State>(
654 }
655
656 function callCallback(callback, context) {
659 - invariant(
660 - typeof callback === 'function',
661 - 'Invalid argument passed as callback. Expected a function. Instead ' +
662 - 'received: %s',
663 - callback,
664 - );
657 + if (typeof callback !== 'function') {
658 + throw new Error(
659 + 'Invalid argument passed as callback. Expected a function. Instead ' +
660 + `received: ${callback}`,
661 + );
662 + }
663 +
664 callback.call(context);
665 }
666
packages/react-reconciler/src/ReactUpdateQueue.old.js
+7 -8
@@ -112,8 +112,6 @@ import {
112 import {pushInterleavedQueue} from './ReactFiberInterleavedUpdates.old';
113 import {setIsStrictModeForDevtools} from './ReactFiberDevToolsHook.old';
114
115 -import invariant from 'shared/invariant';
116 -
115 export type Update<State> = {|
116 // TODO: Temporary field. Will remove this by storing a map of
117 // transition -> event time on the root.
@@ -656,12 +654,13 @@ export function processUpdateQueue<State>(
654 }
655
656 function callCallback(callback, context) {
659 - invariant(
660 - typeof callback === 'function',
661 - 'Invalid argument passed as callback. Expected a function. Instead ' +
662 - 'received: %s',
663 - callback,
664 - );
657 + if (typeof callback !== 'function') {
658 + throw new Error(
659 + 'Invalid argument passed as callback. Expected a function. Instead ' +
660 + `received: ${callback}`,
661 + );
662 + }
663 +
664 callback.call(context);
665 }
666
packages/react-reconciler/src/forks/ReactFiberErrorDialog.native.js
+5 -6
@@ -15,12 +15,11 @@ import {ClassComponent} from '../ReactWorkTags';
15 // Module provided by RN:
16 import {ReactFiberErrorDialog as RNImpl} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface';
17
18 -import invariant from 'shared/invariant';
19 -
20 -invariant(
21 - typeof RNImpl.showErrorDialog === 'function',
22 - 'Expected ReactFiberErrorDialog.showErrorDialog to be a function.',
23 -);
18 +if (typeof RNImpl.showErrorDialog !== 'function') {
19 + throw new Error(
20 + 'Expected ReactFiberErrorDialog.showErrorDialog to be a function.',
21 + );
22 +}
23
24 export function showErrorDialog(
25 boundary: Fiber,
packages/react-reconciler/src/forks/ReactFiberErrorDialog.www.js
+6 -6
@@ -12,14 +12,14 @@ import type {CapturedValue} from '../ReactCapturedValue';
12
13 import {ClassComponent} from '../ReactWorkTags';
14
15 -import invariant from 'shared/invariant';
16 -
15 // Provided by www
16 const ReactFiberErrorDialogWWW = require('ReactFiberErrorDialog');
19 -invariant(
20 - typeof ReactFiberErrorDialogWWW.showErrorDialog === 'function',
21 - 'Expected ReactFiberErrorDialog.showErrorDialog to be a function.',
22 -);
17 +
18 +if (typeof ReactFiberErrorDialogWWW.showErrorDialog !== 'function') {
19 + throw new Error(
20 + 'Expected ReactFiberErrorDialog.showErrorDialog to be a function.',
21 + );
22 +}
23
24 export function showErrorDialog(
25 boundary: Fiber,
packages/react-server/src/ReactFizzContext.js
+6 -7
@@ -9,7 +9,6 @@
9
10 import {disableLegacyContext} from 'shared/ReactFeatureFlags';
11 import getComponentNameFromType from 'shared/getComponentNameFromType';
12 -import invariant from 'shared/invariant';
12 import checkPropTypes from 'shared/checkPropTypes';
13
14 let warnedAboutMissingGetChildContext;
@@ -77,12 +76,12 @@ export function processChildContext(
76
77 const childContext = instance.getChildContext();
78 for (const contextKey in childContext) {
80 - invariant(
81 - contextKey in childContextTypes,
82 - '%s.getChildContext(): key "%s" is not defined in childContextTypes.',
83 - getComponentNameFromType(type) || 'Unknown',
84 - contextKey,
85 - );
79 + if (!(contextKey in childContextTypes)) {
80 + throw new Error(
81 + `${getComponentNameFromType(type) ||
82 + 'Unknown'}.getChildContext(): key "${contextKey}" is not defined in childContextTypes.`,
83 + );
84 + }
85 }
86 if (__DEV__) {
87 const name = getComponentNameFromType(type) || 'Unknown';
packages/react-server/src/ReactFizzHooks.js
+22 -21
@@ -22,7 +22,6 @@ import {readContext as readContextImpl} from './ReactFizzNewContext';
22
23 import {makeServerID} from './ReactServerFormatConfig';
24
25 -import invariant from 'shared/invariant';
25 import {enableCache} from 'shared/ReactFeatureFlags';
26 import is from 'shared/objectIs';
27
@@ -64,15 +63,17 @@ let isInHookUserCodeInDev = false;
63 let currentHookNameInDev: ?string;
64
65 function resolveCurrentlyRenderingComponent(): Object {
67 - invariant(
68 - currentlyRenderingComponent !== null,
69 - 'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for' +
70 - ' one of the following reasons:\n' +
71 - '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' +
72 - '2. You might be breaking the Rules of Hooks\n' +
73 - '3. You might have more than one copy of React in the same app\n' +
74 - 'See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.',
75 - );
66 + if (currentlyRenderingComponent === null) {
67 + throw new Error(
68 + 'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for' +
69 + ' one of the following reasons:\n' +
70 + '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' +
71 + '2. You might be breaking the Rules of Hooks\n' +
72 + '3. You might have more than one copy of React in the same app\n' +
73 + 'See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.',
74 + );
75 + }
76 +
77 if (__DEV__) {
78 if (isInHookUserCodeInDev) {
79 console.error(
@@ -128,7 +129,7 @@ function areHookInputsEqual(
129
130 function createHook(): Hook {
131 if (numberOfReRenders > 0) {
131 - invariant(false, 'Rendered more hooks than during the previous render');
132 + throw new Error('Rendered more hooks than during the previous render');
133 }
134 return {
135 memoizedState: null,
@@ -219,7 +220,7 @@ export function resetHooksState(): void {
220 function getCacheForType<T>(resourceType: () => T): T {
221 // TODO: This should silently mark this as client rendered since it's not necesssarily
222 // considered an error. It needs to work for things like Flight though.
222 - invariant(false, 'Not implemented.');
223 + throw new Error('Not implemented.');
224 }
225
226 function readContext<T>(context: ReactContext<T>): T {
@@ -406,11 +407,12 @@ function dispatchAction<A>(
407 queue: UpdateQueue<A>,
408 action: A,
409 ) {
409 - invariant(
410 - numberOfReRenders < RE_RENDER_LIMIT,
411 - 'Too many re-renders. React limits the number of renders to prevent ' +
412 - 'an infinite loop.',
413 - );
410 + if (numberOfReRenders >= RE_RENDER_LIMIT) {
411 + throw new Error(
412 + 'Too many re-renders. React limits the number of renders to prevent ' +
413 + 'an infinite loop.',
414 + );
415 + }
416
417 if (componentIdentity === currentlyRenderingComponent) {
418 // This is a render phase update. Stash it in a lazily-created map of
@@ -467,8 +469,7 @@ function useSyncExternalStore<T>(
469 getServerSnapshot?: () => T,
470 ): T {
471 if (getServerSnapshot === undefined) {
470 - invariant(
471 - false,
472 + throw new Error(
473 'Missing getServerSnapshot, which is required for ' +
474 'server-rendered content. Will revert to client rendering.',
475 );
@@ -482,7 +483,7 @@ function useDeferredValue<T>(value: T): T {
483 }
484
485 function unsupportedStartTransition() {
485 - invariant(false, 'startTransition cannot be called during server rendering.');
486 + throw new Error('startTransition cannot be called during server rendering.');
487 }
488
489 function useTransition(): [boolean, (callback: () => void) => void] {
@@ -495,7 +496,7 @@ function useOpaqueIdentifier(): OpaqueIDType {
496 }
497
498 function unsupportedRefresh() {
498 - invariant(false, 'Cache cannot be refreshed during server rendering.');
499 + throw new Error('Cache cannot be refreshed during server rendering.');
500 }
501
502 function useCacheRefresh(): <T>(?() => T, ?T) => void {
packages/react-server/src/ReactFizzNewContext.js
+32 -22
@@ -11,8 +11,6 @@ import type {ReactContext} from 'shared/ReactTypes';
11
12 import {isPrimaryRenderer} from './ReactServerFormatConfig';
13
14 -import invariant from 'shared/invariant';
15 -
14 let rendererSigil;
15 if (__DEV__) {
16 // Use this to detect multiple renderers using the same context
@@ -67,15 +65,18 @@ function popToNearestCommonAncestor(
65 const parentPrev = prev.parent;
66 const parentNext = next.parent;
67 if (parentPrev === null) {
70 - invariant(
71 - parentNext === null,
72 - 'The stacks must reach the root at the same time. This is a bug in React.',
73 - );
68 + if (parentNext !== null) {
69 + throw new Error(
70 + 'The stacks must reach the root at the same time. This is a bug in React.',
71 + );
72 + }
73 } else {
75 - invariant(
76 - parentNext !== null,
77 - 'The stacks must reach the root at the same time. This is a bug in React.',
78 - );
74 + if (parentNext === null) {
75 + throw new Error(
76 + 'The stacks must reach the root at the same time. This is a bug in React.',
77 + );
78 + }
79 +
80 popToNearestCommonAncestor(parentPrev, parentNext);
81 // On the way back, we push the new ones that weren't common.
82 pushNode(next);
@@ -105,10 +106,13 @@ function popPreviousToCommonLevel(
106 ): void {
107 popNode(prev);
108 const parentPrev = prev.parent;
108 - invariant(
109 - parentPrev !== null,
110 - 'The depth must equal at least at zero before reaching the root. This is a bug in React.',
111 - );
109 +
110 + if (parentPrev === null) {
111 + throw new Error(
112 + 'The depth must equal at least at zero before reaching the root. This is a bug in React.',
113 + );
114 + }
115 +
116 if (parentPrev.depth === next.depth) {
117 // We found the same level. Now we just need to find a shared ancestor.
118 popToNearestCommonAncestor(parentPrev, next);
@@ -123,10 +127,13 @@ function popNextToCommonLevel(
127 next: ContextNode<any>,
128 ): void {
129 const parentNext = next.parent;
126 - invariant(
127 - parentNext !== null,
128 - 'The depth must equal at least at zero before reaching the root. This is a bug in React.',
129 - );
130 +
131 + if (parentNext === null) {
132 + throw new Error(
133 + 'The depth must equal at least at zero before reaching the root. This is a bug in React.',
134 + );
135 + }
136 +
137 if (prev.depth === parentNext.depth) {
138 // We found the same level. Now we just need to find a shared ancestor.
139 popToNearestCommonAncestor(prev, parentNext);
@@ -221,10 +228,13 @@ export function pushProvider<T>(
228
229 export function popProvider<T>(context: ReactContext<T>): ContextSnapshot {
230 const prevSnapshot = currentActiveSnapshot;
224 - invariant(
225 - prevSnapshot !== null,
226 - 'Tried to pop a Context at the root of the app. This is a bug in React.',
227 - );
231 +
232 + if (prevSnapshot === null) {
233 + throw new Error(
234 + 'Tried to pop a Context at the root of the app. This is a bug in React.',
235 + );
236 + }
237 +
238 if (__DEV__) {
239 if (prevSnapshot.context !== context) {
240 console.error(
packages/react-server/src/ReactFizzServer.js
+33 -29
@@ -113,7 +113,6 @@ import {
113 } from 'shared/ReactFeatureFlags';
114
115 import getComponentNameFromType from 'shared/getComponentNameFromType';
116 -import invariant from 'shared/invariant';
116 import isArray from 'shared/isArray';
117
118 const ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
@@ -984,7 +983,7 @@ function renderElement(
983 renderNodeDestructive(request, task, props.children);
984 return;
985 }
987 - invariant(false, 'ReactDOMServer does not yet support scope components.');
986 + throw new Error('ReactDOMServer does not yet support scope components.');
987 }
988 // eslint-disable-next-line-no-fallthrough
989 case REACT_SUSPENSE_TYPE: {
@@ -1039,13 +1038,11 @@ function renderElement(
1038 'named imports.';
1039 }
1040 }
1042 - invariant(
1043 - false,
1041 +
1042 + throw new Error(
1043 'Element type is invalid: expected a string (for built-in ' +
1044 'components) or a class/function (for composite components) ' +
1046 - 'but got: %s.%s',
1047 - type == null ? type : typeof type,
1048 - info,
1045 + `but got: ${type == null ? type : typeof type}.${info}`,
1046 );
1047 }
1048
@@ -1106,8 +1103,7 @@ function renderNodeDestructive(
1103 return;
1104 }
1105 case REACT_PORTAL_TYPE:
1109 - invariant(
1110 - false,
1106 + throw new Error(
1107 'Portals are not currently supported by the server renderer. ' +
1108 'Render them conditionally so that they only appear on the client render.',
1109 );
@@ -1157,14 +1153,15 @@ function renderNodeDestructive(
1153 }
1154
1155 const childString = Object.prototype.toString.call(node);
1160 - invariant(
1161 - false,
1162 - 'Objects are not valid as a React child (found: %s). ' +
1156 +
1157 + throw new Error(
1158 + `Objects are not valid as a React child (found: ${
1159 + childString === '[object Object]'
1160 + ? 'object with keys {' + Object.keys(node).join(', ') + '}'
1161 + : childString
1162 + }). ` +
1163 'If you meant to render a collection of children, use an array ' +
1164 'instead.',
1165 - childString === '[object Object]'
1166 - ? 'object with keys {' + Object.keys(node).join(', ') + '}'
1167 - : childString,
1165 );
1166 }
1167
@@ -1370,10 +1367,12 @@ function finishedTask(
1367 ) {
1368 if (boundary === null) {
1369 if (segment.parentFlushed) {
1373 - invariant(
1374 - request.completedRootSegment === null,
1375 - 'There can only be one root segment. This is a bug in React.',
1376 - );
1370 + if (request.completedRootSegment !== null) {
1371 + throw new Error(
1372 + 'There can only be one root segment. This is a bug in React.',
1373 + );
1374 + }
1375 +
1376 request.completedRootSegment = segment;
1377 }
1378 request.pendingRootTasks--;
@@ -1557,8 +1556,7 @@ function flushSubtree(
1556 return r;
1557 }
1558 default: {
1560 - invariant(
1561 - false,
1559 + throw new Error(
1560 'Aborted, errored or already flushed boundaries should not be flushed again. This is a bug in React.',
1561 );
1562 }
@@ -1640,10 +1638,13 @@ function flushSegment(
1638 writeStartCompletedSuspenseBoundary(destination, request.responseState);
1639
1640 const completedSegments = boundary.completedSegments;
1643 - invariant(
1644 - completedSegments.length === 1,
1645 - 'A previously unvisited boundary must have exactly one root segment. This is a bug in React.',
1646 - );
1641 +
1642 + if (completedSegments.length !== 1) {
1643 + throw new Error(
1644 + 'A previously unvisited boundary must have exactly one root segment. This is a bug in React.',
1645 + );
1646 + }
1647 +
1648 const contentSegment = completedSegments[0];
1649 flushSegment(request, destination, contentSegment);
1650
@@ -1741,10 +1742,13 @@ function flushPartiallyCompletedSegment(
1742 // This segment wasn't previously referred to. This happens at the root of
1743 // a boundary. We make kind of a leap here and assume this is the root.
1744 const rootSegmentID = (segment.id = boundary.rootSegmentID);
1744 - invariant(
1745 - rootSegmentID !== -1,
1746 - 'A root segment ID must have been assigned by now. This is a bug in React.',
1747 - );
1745 +
1746 + if (rootSegmentID === -1) {
1747 + throw new Error(
1748 + 'A root segment ID must have been assigned by now. This is a bug in React.',
1749 + );
1750 + }
1751 +
1752 return flushSegmentContainer(request, destination, segment);
1753 } else {
1754 flushSegmentContainer(request, destination, segment);
packages/react-server/src/ReactFlightServer.js
+54 -52
@@ -43,7 +43,6 @@ import {
43 } from 'shared/ReactSymbols';
44
45 import ReactSharedInternals from 'shared/ReactSharedInternals';
46 -import invariant from 'shared/invariant';
46 import isArray from 'shared/isArray';
47
48 type ReactJSONValue =
@@ -141,8 +140,7 @@ function attemptResolveElement(
140 // When the ref moves to the regular props object this will implicitly
141 // throw for functions. We could probably relax it to a DEV warning for other
142 // cases.
144 - invariant(
145 - false,
143 + throw new Error(
144 'Refs cannot be used in server components, nor passed to client components.',
145 );
146 }
@@ -178,10 +176,8 @@ function attemptResolveElement(
176 }
177 }
178 }
181 - invariant(
182 - false,
183 - 'Unsupported server component type: %s',
184 - describeValueForErrorMessage(type),
179 + throw new Error(
180 + `Unsupported server component type: ${describeValueForErrorMessage(type)}`,
181 );
182 }
183
@@ -395,8 +391,7 @@ export function resolveModelToJSON(
391 case REACT_ELEMENT_TYPE:
392 return '$';
393 case REACT_LAZY_TYPE:
398 - invariant(
399 - false,
394 + throw new Error(
395 'React Lazy Components are not yet supported on the server.',
396 );
397 }
@@ -537,23 +532,25 @@ export function resolveModelToJSON(
532
533 if (typeof value === 'function') {
534 if (/^on[A-Z]/.test(key)) {
540 - invariant(
541 - false,
535 + throw new Error(
536 'Event handlers cannot be passed to client component props. ' +
543 - 'Remove %s from these props if possible: %s\n' +
537 + `Remove ${describeKeyForErrorMessage(
538 + key,
539 + )} from these props if possible: ${describeObjectForErrorMessage(
540 + parent,
541 + )}
542 +` +
543 'If you need interactivity, consider converting part of this to a client component.',
545 - describeKeyForErrorMessage(key),
546 - describeObjectForErrorMessage(parent),
544 );
545 } else {
549 - invariant(
550 - false,
546 + throw new Error(
547 'Functions cannot be passed directly to client components ' +
548 "because they're not serializable. " +
553 - 'Remove %s (%s) from this object, or avoid the entire object: %s',
554 - describeKeyForErrorMessage(key),
555 - value.displayName || value.name || 'function',
556 - describeObjectForErrorMessage(parent),
549 + `Remove ${describeKeyForErrorMessage(key)} (${value.displayName ||
550 + value.name ||
551 + 'function'}) from this object, or avoid the entire object: ${describeObjectForErrorMessage(
552 + parent,
553 + )}`,
554 );
555 }
556 }
@@ -565,15 +562,19 @@ export function resolveModelToJSON(
562 return serializeByValueID(existingId);
563 }
564 const name = value.description;
568 - invariant(
569 - Symbol.for(name) === value,
570 - 'Only global symbols received from Symbol.for(...) can be passed to client components. ' +
571 - 'The symbol Symbol.for(%s) cannot be found among global symbols. ' +
572 - 'Remove %s from this object, or avoid the entire object: %s',
573 - value.description,
574 - describeKeyForErrorMessage(key),
575 - describeObjectForErrorMessage(parent),
576 - );
565 +
566 + if (Symbol.for(name) !== value) {
567 + throw new Error(
568 + 'Only global symbols received from Symbol.for(...) can be passed to client components. ' +
569 + `The symbol Symbol.for(${value.description}) cannot be found among global symbols. ` +
570 + `Remove ${describeKeyForErrorMessage(
571 + key,
572 + )} from this object, or avoid the entire object: ${describeObjectForErrorMessage(
573 + parent,
574 + )}`,
575 + );
576 + }
577 +
578 request.pendingChunks++;
579 const symbolId = request.nextChunkId++;
580 emitSymbolChunk(request, symbolId, name);
@@ -583,23 +584,23 @@ export function resolveModelToJSON(
584
585 // $FlowFixMe: bigint isn't added to Flow yet.
586 if (typeof value === 'bigint') {
586 - invariant(
587 - false,
588 - 'BigInt (%s) is not yet supported in client component props. ' +
589 - 'Remove %s from this object or use a plain number instead: %s',
590 - value,
591 - describeKeyForErrorMessage(key),
592 - describeObjectForErrorMessage(parent),
587 + throw new Error(
588 + `BigInt (${value}) is not yet supported in client component props. ` +
589 + `Remove ${describeKeyForErrorMessage(
590 + key,
591 + )} from this object or use a plain number instead: ${describeObjectForErrorMessage(
592 + parent,
593 + )}`,
594 );
595 }
596
596 - invariant(
597 - false,
598 - 'Type %s is not supported in client component props. ' +
599 - 'Remove %s from this object, or avoid the entire object: %s',
600 - typeof value,
601 - describeKeyForErrorMessage(key),
602 - describeObjectForErrorMessage(parent),
597 + throw new Error(
598 + `Type ${typeof value} is not supported in client component props. ` +
599 + `Remove ${describeKeyForErrorMessage(
600 + key,
601 + )} from this object, or avoid the entire object: ${describeObjectForErrorMessage(
602 + parent,
603 + )}`,
604 );
605 }
606
@@ -799,14 +800,15 @@ export function startFlowing(request: Request, destination: Destination): void {
800 }
801
802 function unsupportedHook(): void {
802 - invariant(false, 'This Hook is not supported in Server Components.');
803 + throw new Error('This Hook is not supported in Server Components.');
804 }
805
806 function unsupportedRefresh(): void {
806 - invariant(
807 - currentCache,
808 - 'Refreshing the cache is not supported in Server Components.',
809 - );
807 + if (!currentCache) {
808 + throw new Error(
809 + 'Refreshing the cache is not supported in Server Components.',
810 + );
811 + }
812 }
813
814 let currentCache: Map<Function, mixed> | null = null;
@@ -822,10 +824,10 @@ const Dispatcher: DispatcherType = {
824 useDeferredValue: (unsupportedHook: any),
825 useTransition: (unsupportedHook: any),
826 getCacheForType<T>(resourceType: () => T): T {
825 - invariant(
826 - currentCache,
827 - 'Reading the cache is only supported while rendering.',
828 - );
827 + if (!currentCache) {
828 + throw new Error('Reading the cache is only supported while rendering.');
829 + }
830 +
831 let entry: T | void = (currentCache.get(resourceType): any);
832 if (entry === undefined) {
833 entry = resourceType();
packages/react-server/src/ReactFlightServerConfig.js
+2 -4
@@ -7,9 +7,7 @@
7 * @flow
8 */
9
10 -/* eslint-disable react-internal/invariant-args */
11 -
12 -import invariant from 'shared/invariant';
10 +/* eslint-disable react-internal/prod-error-codes */
11
12 // We expect that our Rollup, Jest, and Flow configurations
13 // always shim this module with the corresponding host config
@@ -19,4 +17,4 @@ import invariant from 'shared/invariant';
17 // sure that if we *do* accidentally break the configuration,
18 // the failure isn't silent.
19
22 -invariant(false, 'This module must be shimmed by a specific renderer.');
20 +throw new Error('This module must be shimmed by a specific renderer.');
packages/react-server/src/ReactServerFormatConfig.js
+2 -4
@@ -7,9 +7,7 @@
7 * @flow
8 */
9
10 -/* eslint-disable react-internal/invariant-args */
11 -
12 -import invariant from 'shared/invariant';
10 +/* eslint-disable react-internal/prod-error-codes */
11
12 // We expect that our Rollup, Jest, and Flow configurations
13 // always shim this module with the corresponding host config
@@ -19,4 +17,4 @@ import invariant from 'shared/invariant';
17 // sure that if we *do* accidentally break the configuration,
18 // the failure isn't silent.
19
22 -invariant(false, 'This module must be shimmed by a specific renderer.');
20 +throw new Error('This module must be shimmed by a specific renderer.');
packages/react-server/src/ReactServerStreamConfig.js
+2 -4
@@ -7,9 +7,7 @@
7 * @flow
8 */
9
10 -/* eslint-disable react-internal/invariant-args */
11 -
12 -import invariant from 'shared/invariant';
10 +/* eslint-disable react-internal/prod-error-codes */
11
12 // We expect that our Rollup, Jest, and Flow configurations
13 // always shim this module with the corresponding host config
@@ -19,4 +17,4 @@ import invariant from 'shared/invariant';
17 // sure that if we *do* accidentally break the configuration,
18 // the failure isn't silent.
19
22 -invariant(false, 'This module must be shimmed by a specific renderer.');
20 +throw new Error('This module must be shimmed by a specific renderer.');
packages/react-suspense-test-utils/src/ReactSuspenseTestUtils.js
+1 -2
@@ -9,12 +9,11 @@
9
10 import type {Dispatcher} from 'react-reconciler/src/ReactInternalTypes';
11 import ReactSharedInternals from 'shared/ReactSharedInternals';
12 -import invariant from 'shared/invariant';
12
13 const ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
14
15 function unsupported() {
17 - invariant(false, 'This feature is not supported by ReactSuspenseTestUtils.');
16 + throw new Error('This feature is not supported by ReactSuspenseTestUtils.');
17 }
18
19 export function waitForSuspense<T>(fn: () => T): Promise<T> {
packages/react-test-renderer/src/ReactTestRenderer.js
+22 -17
@@ -40,7 +40,6 @@ import {
40 IncompleteClassComponent,
41 ScopeComponent,
42 } from 'react-reconciler/src/ReactWorkTags';
43 -import invariant from 'shared/invariant';
43 import isArray from 'shared/isArray';
44 import getComponentNameFromType from 'shared/getComponentNameFromType';
45 import ReactVersion from 'shared/ReactVersion';
@@ -219,10 +218,8 @@ function toTree(node: ?Fiber) {
218 case ScopeComponent:
219 return childrenToTree(node.child);
220 default:
222 - invariant(
223 - false,
224 - 'toTree() does not yet know how to handle nodes with tag=%s',
225 - node.tag,
221 + throw new Error(
222 + `toTree() does not yet know how to handle nodes with tag=${node.tag}`,
223 );
224 }
225 }
@@ -282,21 +279,25 @@ class ReactTestInstance {
279 _currentFiber(): Fiber {
280 // Throws if this component has been unmounted.
281 const fiber = findCurrentFiberUsingSlowPath(this._fiber);
285 - invariant(
286 - fiber !== null,
287 - "Can't read from currently-mounting component. This error is likely " +
288 - 'caused by a bug in React. Please file an issue.',
289 - );
282 +
283 + if (fiber === null) {
284 + throw new Error(
285 + "Can't read from currently-mounting component. This error is likely " +
286 + 'caused by a bug in React. Please file an issue.',
287 + );
288 + }
289 +
290 return fiber;
291 }
292
293 constructor(fiber: Fiber) {
294 - invariant(
295 - validWrapperTypes.has(fiber.tag),
296 - 'Unexpected object passed to ReactTestInstance constructor (tag: %s). ' +
297 - 'This is probably a bug in React.',
298 - fiber.tag,
299 - );
294 + if (!validWrapperTypes.has(fiber.tag)) {
295 + throw new Error(
296 + `Unexpected object passed to ReactTestInstance constructor (tag: ${fiber.tag}). ` +
297 + 'This is probably a bug in React.',
298 + );
299 + }
300 +
301 this._fiber = fiber;
302 }
303
@@ -471,7 +472,11 @@ function create(element: React$Element<any>, options: TestRendererOptions) {
472 isStrictMode,
473 concurrentUpdatesByDefault,
474 );
474 - invariant(root != null, 'something went wrong');
475 +
476 + if (root == null) {
477 + throw new Error('something went wrong');
478 + }
479 +
480 updateContainer(element, root, null, null);
481
482 const entry = {
packages/react/src/ReactAct.js
+1 -5
@@ -9,7 +9,6 @@
9
10 import type {Thenable} from 'shared/ReactTypes';
11 import ReactCurrentActQueue from './ReactCurrentActQueue';
12 -import invariant from 'shared/invariant';
12 import enqueueTask from 'shared/enqueueTask';
13
14 let actScopeDepth = 0;
@@ -151,10 +150,7 @@ export function act<T>(callback: () => T | Thenable<T>): Thenable<T> {
150 }
151 }
152 } else {
154 - invariant(
155 - false,
156 - 'act(...) is not supported in production builds of React.',
157 - );
153 + throw new Error('act(...) is not supported in production builds of React.');
154 }
155 }
156
packages/react/src/ReactBaseClasses.js
+11 -9
@@ -5,8 +5,6 @@
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 -import invariant from 'shared/invariant';
9 -
8 import ReactNoopUpdateQueue from './ReactNoopUpdateQueue';
9
10 const emptyObject = {};
@@ -55,13 +53,17 @@ Component.prototype.isReactComponent = {};
53 * @protected
54 */
55 Component.prototype.setState = function(partialState, callback) {
58 - invariant(
59 - typeof partialState === 'object' ||
60 - typeof partialState === 'function' ||
61 - partialState == null,
62 - 'setState(...): takes an object of state variables to update or a ' +
63 - 'function which returns an object of state variables.',
64 - );
56 + if (
57 + typeof partialState !== 'object' &&
58 + typeof partialState !== 'function' &&
59 + partialState != null
60 + ) {
61 + throw new Error(
62 + 'setState(...): takes an object of state variables to update or a ' +
63 + 'function which returns an object of state variables.',
64 + );
65 + }
66 +
67 this.updater.enqueueSetState(this, partialState, callback, 'setState');
68 };
69
packages/react/src/ReactChildren.js
+15 -11
@@ -9,7 +9,6 @@
9
10 import type {ReactNodeList} from 'shared/ReactTypes';
11
12 -import invariant from 'shared/invariant';
12 import isArray from 'shared/isArray';
13 import {
14 getIteratorFn,
@@ -205,14 +204,17 @@ function mapIntoArray(
204 } else if (type === 'object') {
205 // eslint-disable-next-line react-internal/safe-string-coercion
206 const childrenString = String((children: any));
208 - invariant(
209 - false,
210 - 'Objects are not valid as a React child (found: %s). ' +
207 +
208 + throw new Error(
209 + `Objects are not valid as a React child (found: ${
210 + childrenString === '[object Object]'
211 + ? 'object with keys {' +
212 + Object.keys((children: any)).join(', ') +
213 + '}'
214 + : childrenString
215 + }). ` +
216 'If you meant to render a collection of children, use an array ' +
217 'instead.',
213 - childrenString === '[object Object]'
214 - ? 'object with keys {' + Object.keys((children: any)).join(', ') + '}'
215 - : childrenString,
218 );
219 }
220 }
@@ -323,10 +325,12 @@ function toArray(children: ?ReactNodeList): Array<React$Node> {
325 * structure.
326 */
327 function onlyChild<T>(children: T): T {
326 - invariant(
327 - isValidElement(children),
328 - 'React.Children.only expected to receive a single React element child.',
329 - );
328 + if (!isValidElement(children)) {
329 + throw new Error(
330 + 'React.Children.only expected to receive a single React element child.',
331 + );
332 + }
333 +
334 return children;
335 }
336
packages/react/src/ReactElement.js
+5 -6
@@ -6,7 +6,6 @@
6 */
7
8 import getComponentNameFromType from 'shared/getComponentNameFromType';
9 -import invariant from 'shared/invariant';
9 import {REACT_ELEMENT_TYPE} from 'shared/ReactSymbols';
10 import hasOwnProperty from 'shared/hasOwnProperty';
11 import {checkKeyStringCoercion} from 'shared/CheckStringCoercion';
@@ -484,11 +483,11 @@ export function cloneAndReplaceKey(oldElement, newKey) {
483 * See https://reactjs.org/docs/react-api.html#cloneelement
484 */
485 export function cloneElement(element, config, children) {
487 - invariant(
488 - !(element === null || element === undefined),
489 - 'React.cloneElement(...): The argument must be a React element, but you passed %s.',
490 - element,
491 - );
486 + if (element === null || element === undefined) {
487 + throw new Error(
488 + `React.cloneElement(...): The argument must be a React element, but you passed ${element}.`,
489 + );
490 + }
491
492 let propName;
493
packages/react/unstable-shared-subset.js
+1
@@ -7,6 +7,7 @@
7 * @flow
8 */
9
10 +// eslint-disable-next-line react-internal/prod-error-codes
11 throw new Error(
12 'This entry point is not yet supported outside of experimental channels',
13 );
packages/scheduler/src/forks/SchedulerMock.js
+1
@@ -7,6 +7,7 @@
7 */
8
9 /* eslint-disable no-var */
10 +/* eslint-disable react-internal/prod-error-codes */
11
12 import {
13 enableSchedulerDebugging,
packages/shared/ReactErrorUtils.js
+1 -3
@@ -7,7 +7,6 @@
7 * @flow
8 */
9
10 -import invariant from 'shared/invariant';
10 import invokeGuardedCallbackImpl from './invokeGuardedCallbackImpl';
11
12 // Used by Fiber to simulate a try-catch.
@@ -117,8 +116,7 @@ export function clearCaughtError() {
116 caughtError = null;
117 return error;
118 } else {
120 - invariant(
121 - false,
119 + throw new Error(
120 'clearCaughtError was called but no error was captured. This error ' +
121 'is likely caused by a bug in React. Please file an issue.',
122 );
packages/shared/checkPropTypes.js
+1
@@ -52,6 +52,7 @@ export default function checkPropTypes(
52 // This is intentionally an invariant that gets caught. It's the same
53 // behavior as without this statement except with a better message.
54 if (typeof typeSpecs[typeSpecName] !== 'function') {
55 + // eslint-disable-next-line react-internal/prod-error-codes
56 const err = Error(
57 (componentName || 'React class') +
58 ': ' +
packages/shared/forks/invokeGuardedCallbackImpl.www.js
+6 -6
@@ -5,14 +5,14 @@
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 -import invariant from 'shared/invariant';
9 -
8 // Provided by www
9 const ReactFbErrorUtils = require('ReactFbErrorUtils');
12 -invariant(
13 - typeof ReactFbErrorUtils.invokeGuardedCallback === 'function',
14 - 'Expected ReactFbErrorUtils.invokeGuardedCallback to be a function.',
15 -);
10 +
11 +if (typeof ReactFbErrorUtils.invokeGuardedCallback !== 'function') {
12 + throw new Error(
13 + 'Expected ReactFbErrorUtils.invokeGuardedCallback to be a function.',
14 + );
15 +}
16
17 const invokeGuardedCallbackImpl = function<A, B, C, D, E, F, Context>(
18 name: string | null,
packages/shared/invariant.js
+1
@@ -18,6 +18,7 @@
18 */
19
20 export default function invariant(condition, format, a, b, c, d, e, f) {
21 + // eslint-disable-next-line react-internal/prod-error-codes
22 throw new Error(
23 'Internal React error: invariant() is meant to be replaced at compile ' +
24 'time. There is no runtime version.',
packages/shared/invokeGuardedCallbackImpl.js
+14 -12
@@ -7,8 +7,6 @@
7 * @flow
8 */
9
10 -import invariant from 'shared/invariant';
11 -
10 function invokeGuardedCallbackProd<A, B, C, D, E, F, Context>(
11 name: string | null,
12 func: (a: A, b: B, c: C, d: D, e: E, f: F) => mixed,
@@ -83,16 +81,18 @@ if (__DEV__) {
81 // when we call document.createEvent(). However this can cause confusing
82 // errors: https://github.com/facebook/create-react-app/issues/3482
83 // So we preemptively throw with a better message instead.
86 - invariant(
87 - typeof document !== 'undefined',
88 - 'The `document` global was defined when React was initialized, but is not ' +
89 - 'defined anymore. This can happen in a test environment if a component ' +
90 - 'schedules an update from an asynchronous callback, but the test has already ' +
91 - 'finished running. To solve this, you can either unmount the component at ' +
92 - 'the end of your test (and ensure that any asynchronous operations get ' +
93 - 'canceled in `componentWillUnmount`), or you can change the test itself ' +
94 - 'to be asynchronous.',
95 - );
84 + if (typeof document === 'undefined') {
85 + throw new Error(
86 + 'The `document` global was defined when React was initialized, but is not ' +
87 + 'defined anymore. This can happen in a test environment if a component ' +
88 + 'schedules an update from an asynchronous callback, but the test has already ' +
89 + 'finished running. To solve this, you can either unmount the component at ' +
90 + 'the end of your test (and ensure that any asynchronous operations get ' +
91 + 'canceled in `componentWillUnmount`), or you can change the test itself ' +
92 + 'to be asynchronous.',
93 + );
94 + }
95 +
96 const evt = document.createEvent('Event');
97
98 let didCall = false;
@@ -201,6 +201,7 @@ if (__DEV__) {
201 if (didCall && didError) {
202 if (!didSetError) {
203 // The callback errored, but the error event never fired.
204 + // eslint-disable-next-line react-internal/prod-error-codes
205 error = new Error(
206 'An error was thrown inside one of your components, but React ' +
207 "doesn't know what it was. This is likely due to browser " +
@@ -212,6 +213,7 @@ if (__DEV__) {
213 'actually an issue with React, please file an issue.',
214 );
215 } else if (isCrossOriginError) {
216 + // eslint-disable-next-line react-internal/prod-error-codes
217 error = new Error(
218 "A cross-origin error was thrown. React doesn't have access to " +
219 'the actual error object in development. ' +
scripts/error-codes/__tests__/__snapshots__/transform-error-messages.js.snap
+35
@@ -1,5 +1,10 @@
1 // Jest Snapshot v1, https://goo.gl/fbAQLP
2
3 +exports[`error transform handles escaped backticks in template string 1`] = `
4 +"import _formatProdErrorMessage from \\"shared/formatProdErrorMessage\\";
5 +Error(__DEV__ ? \\"Expected \`\\" + listener + \\"\` listener to be a function, instead got a value of \`\\" + type + \\"\` type.\\" : _formatProdErrorMessage(231, listener, type));"
6 +`;
7 +
8 exports[`error transform should correctly transform invariants that are not in the error codes map 1`] = `
9 "import invariant from 'shared/invariant';
10
@@ -18,6 +23,21 @@ if (!condition) {
23 }"
24 `;
25
26 +exports[`error transform should not touch other calls or new expressions 1`] = `
27 +"new NotAnError();
28 +NotAnError();"
29 +`;
30 +
31 +exports[`error transform should replace error constructors (no new) 1`] = `
32 +"import _formatProdErrorMessage from \\"shared/formatProdErrorMessage\\";
33 +Error(__DEV__ ? 'Do not override existing functions.' : _formatProdErrorMessage(16));"
34 +`;
35 +
36 +exports[`error transform should replace error constructors 1`] = `
37 +"import _formatProdErrorMessage from \\"shared/formatProdErrorMessage\\";
38 +Error(__DEV__ ? 'Do not override existing functions.' : _formatProdErrorMessage(16));"
39 +`;
40 +
41 exports[`error transform should replace simple invariant calls 1`] = `
42 "import _formatProdErrorMessage from \\"shared/formatProdErrorMessage\\";
43 import invariant from 'shared/invariant';
@@ -29,6 +49,21 @@ if (!condition) {
49 }"
50 `;
51
52 +exports[`error transform should support error constructors with concatenated messages 1`] = `
53 +"import _formatProdErrorMessage from \\"shared/formatProdErrorMessage\\";
54 +Error(__DEV__ ? \\"Expected \\" + foo + \\" target to \\" + (\\"be an array; got \\" + bar) : _formatProdErrorMessage(7, foo, bar));"
55 +`;
56 +
57 +exports[`error transform should support interpolating arguments with concatenation 1`] = `
58 +"import _formatProdErrorMessage from \\"shared/formatProdErrorMessage\\";
59 +Error(__DEV__ ? 'Expected ' + foo + ' target to be an array; got ' + bar : _formatProdErrorMessage(7, foo, bar));"
60 +`;
61 +
62 +exports[`error transform should support interpolating arguments with template strings 1`] = `
63 +"import _formatProdErrorMessage from \\"shared/formatProdErrorMessage\\";
64 +Error(__DEV__ ? \\"Expected \\" + foo + \\" target to be an array; got \\" + bar : _formatProdErrorMessage(7, foo, bar));"
65 +`;
66 +
67 exports[`error transform should support invariant calls with a concatenated template string and args 1`] = `
68 "import _formatProdErrorMessage from \\"shared/formatProdErrorMessage\\";
69 import invariant from 'shared/invariant';
scripts/error-codes/__tests__/transform-error-messages.js
+57
@@ -93,4 +93,61 @@ invariant(condition, 'Do not override existing functions.');
93 )
94 ).toMatchSnapshot();
95 });
96 +
97 + it('should replace error constructors', () => {
98 + expect(
99 + transform(`
100 +new Error('Do not override existing functions.');
101 +`)
102 + ).toMatchSnapshot();
103 + });
104 +
105 + it('should replace error constructors (no new)', () => {
106 + expect(
107 + transform(`
108 +Error('Do not override existing functions.');
109 +`)
110 + ).toMatchSnapshot();
111 + });
112 +
113 + it('should not touch other calls or new expressions', () => {
114 + expect(
115 + transform(`
116 +new NotAnError();
117 +NotAnError();
118 +`)
119 + ).toMatchSnapshot();
120 + });
121 +
122 + it('should support interpolating arguments with template strings', () => {
123 + expect(
124 + transform(`
125 +new Error(\`Expected \${foo} target to be an array; got \${bar}\`);
126 +`)
127 + ).toMatchSnapshot();
128 + });
129 +
130 + it('should support interpolating arguments with concatenation', () => {
131 + expect(
132 + transform(`
133 +new Error('Expected ' + foo + ' target to be an array; got ' + bar);
134 +`)
135 + ).toMatchSnapshot();
136 + });
137 +
138 + it('should support error constructors with concatenated messages', () => {
139 + expect(
140 + transform(`
141 +new Error(\`Expected \${foo} target to \` + \`be an array; got \${bar}\`);
142 +`)
143 + ).toMatchSnapshot();
144 + });
145 +
146 + it('handles escaped backticks in template string', () => {
147 + expect(
148 + transform(`
149 +new Error(\`Expected \\\`\$\{listener\}\\\` listener to be a function, instead got a value of \\\`\$\{type\}\\\` type.\`);
150 +`)
151 + ).toMatchSnapshot();
152 + });
153 });
scripts/error-codes/codes.json
+9 -4
@@ -287,12 +287,10 @@
287 "288": "It is not supported to run the profiling version of a renderer (for example, `react-dom/profiling`) without also replacing the `schedule/tracing` module with `schedule/tracing-profiling`. Your bundler might have a setting for aliasing both modules. Learn more at https://reactjs.org/link/profiling",
288 "289": "Function components cannot have refs.",
289 "290": "Element ref was specified as a string (%s) but no owner was set. This could happen for one of the following reasons:\n1. You may be adding a ref to a function component\n2. You may be adding a ref to a component that was not created inside a component's render method\n3. You have multiple copies of React loaded\nSee https://reactjs.org/link/refs-must-have-owner for more information.",
290 - "291": "Log of yielded values is not empty. Call expect(Scheduler).toHaveYielded(...) first.",
290 "292": "The matcher `toHaveYielded` expects an instance of React Test Renderer.\n\nTry: expect(Scheduler).toHaveYielded(expectedYields)",
291 "293": "Context can only be read while React is rendering, e.g. inside the render method or getDerivedStateFromProps.",
292 "294": "ReactDOMServer does not yet support Suspense.",
293 "295": "ReactDOMServer does not yet support lazy-loaded components.",
295 - "296": "Log of yielded values is not empty. Call expect(ReactTestRenderer).unstable_toHaveYielded(...) first.",
294 "297": "The matcher `unstable_toHaveYielded` expects an instance of React Test Renderer.\n\nTry: expect(ReactTestRenderer).unstable_toHaveYielded(expectedYields)",
295 "298": "Hooks can only be called inside the body of a function component.",
296 "299": "createRoot(...): Target container is not a DOM element.",
@@ -353,7 +351,7 @@
351 "362": "Could not find React container within specified host subtree.",
352 "363": "Test selector API is not supported by this renderer.",
353 "364": "Invalid host root specified. Should be either a React container or a node with a testname attribute.",
356 - "365": "Invalid selector type %s specified.",
354 + "365": "Invalid selector type specified.",
355 "366": "ReactDOM.createEventHandle: setListener called on an target that did not have a corresponding root. This is likely a bug in React.",
356 "367": "ReactDOM.createEventHandle: setListener called on an element target that is not managed by React. Ensure React rendered the DOM element.",
357 "368": "ReactDOM.createEventHandle: setListener called on an invalid target. Provide a valid EventTarget or an element managed by React.",
@@ -397,5 +395,12 @@
395 "406": "act(...) is not supported in production builds of React.",
396 "407": "Missing getServerSnapshot, which is required for server-rendered content. Will revert to client rendering.",
397 "408": "Missing getServerSnapshot, which is required for server-rendered content.",
400 - "409": "Cannot update an unmounted root."
398 + "409": "Cannot update an unmounted root.",
399 + "410": "%s suspended while rendering, but no fallback UI was specified.\n\nAdd a <Suspense fallback=...> component higher in the tree to provide a loading indicator or placeholder to display.",
400 + "411": "%s suspended while rendering, but no fallback UI was specified.\n\nAdd a <Suspense fallback=...> component higher in the tree to provide a loading indicator or placeholder to display.",
401 + "412": "Connection closed.",
402 + "413": "Expected finished root and lanes to be set. This is a bug in React.",
403 + "414": "Did not expect this call in production. This is a bug in React. Please file an issue.",
404 + "415": "Error parsing the data. It's probably an error code or network corruption.",
405 + "416": "This environment don't support binary chunks."
406 }
scripts/error-codes/extract-errors.js
+2 -2
@@ -10,7 +10,7 @@ const parser = require('@babel/parser');
10 const fs = require('fs');
11 const path = require('path');
12 const traverse = require('@babel/traverse').default;
13 -const evalToString = require('../shared/evalToString');
13 +const {evalStringConcat} = require('../shared/evalToString');
14 const invertObject = require('./invertObject');
15
16 const babylonOptions = {
@@ -75,7 +75,7 @@ module.exports = function(opts) {
75
76 // error messages can be concatenated (`+`) at runtime, so here's a
77 // trivial partial evaluator that interprets the literal value
78 - const errorMsgLiteral = evalToString(node.arguments[1]);
78 + const errorMsgLiteral = evalStringConcat(node.arguments[1]);
79 addToErrorMap(errorMsgLiteral);
80 }
81 },
scripts/error-codes/transform-error-messages.js
+97 -9
@@ -7,20 +7,114 @@
7 'use strict';
8
9 const fs = require('fs');
10 -const evalToString = require('../shared/evalToString');
10 +const {
11 + evalStringConcat,
12 + evalStringAndTemplateConcat,
13 +} = require('../shared/evalToString');
14 const invertObject = require('./invertObject');
15 const helperModuleImports = require('@babel/helper-module-imports');
16
17 +const errorMap = invertObject(
18 + JSON.parse(fs.readFileSync(__dirname + '/codes.json', 'utf-8'))
19 +);
20 +
21 +const SEEN_SYMBOL = Symbol('transform-error-messages.seen');
22 +
23 module.exports = function(babel) {
24 const t = babel.types;
25
26 + // TODO: Instead of outputting __DEV__ conditions, only apply this transform
27 + // in production.
28 const DEV_EXPRESSION = t.identifier('__DEV__');
29
30 + function CallOrNewExpression(path, file) {
31 + // Turns this code:
32 + //
33 + // new Error(`A ${adj} message that contains ${noun}`);
34 + //
35 + // or this code (no constructor):
36 + //
37 + // Error(`A ${adj} message that contains ${noun}`);
38 + //
39 + // into this:
40 + //
41 + // Error(
42 + // __DEV__
43 + // ? `A ${adj} message that contains ${noun}`
44 + // : formatProdErrorMessage(ERR_CODE, adj, noun)
45 + // );
46 + const node = path.node;
47 + if (node[SEEN_SYMBOL]) {
48 + return;
49 + }
50 + node[SEEN_SYMBOL] = true;
51 +
52 + const errorMsgNode = node.arguments[0];
53 + if (errorMsgNode === undefined) {
54 + return;
55 + }
56 +
57 + const errorMsgExpressions = [];
58 + const errorMsgLiteral = evalStringAndTemplateConcat(
59 + errorMsgNode,
60 + errorMsgExpressions
61 + );
62 +
63 + let prodErrorId = errorMap[errorMsgLiteral];
64 + if (prodErrorId === undefined) {
65 + // There is no error code for this message. We use a lint rule to
66 + // enforce that messages can be minified, so assume this is
67 + // intentional and exit gracefully.
68 + return;
69 + }
70 + prodErrorId = parseInt(prodErrorId, 10);
71 +
72 + // Import formatProdErrorMessage
73 + const formatProdErrorMessageIdentifier = helperModuleImports.addDefault(
74 + path,
75 + 'shared/formatProdErrorMessage',
76 + {nameHint: 'formatProdErrorMessage'}
77 + );
78 +
79 + // Outputs:
80 + // formatProdErrorMessage(ERR_CODE, adj, noun);
81 + const prodMessage = t.callExpression(formatProdErrorMessageIdentifier, [
82 + t.numericLiteral(prodErrorId),
83 + ...errorMsgExpressions,
84 + ]);
85 +
86 + // Outputs:
87 + // Error(
88 + // __DEV__
89 + // ? `A ${adj} message that contains ${noun}`
90 + // : formatProdErrorMessage(ERR_CODE, adj, noun)
91 + // );
92 + path.replaceWith(t.callExpression(t.identifier('Error'), [prodMessage]));
93 + path.replaceWith(
94 + t.callExpression(t.identifier('Error'), [
95 + t.conditionalExpression(DEV_EXPRESSION, errorMsgNode, prodMessage),
96 + ])
97 + );
98 + }
99 +
100 return {
101 visitor: {
102 + NewExpression(path, file) {
103 + const noMinify = file.opts.noMinify;
104 + if (!noMinify && path.get('callee').isIdentifier({name: 'Error'})) {
105 + CallOrNewExpression(path, file);
106 + }
107 + },
108 +
109 CallExpression(path, file) {
110 const node = path.node;
111 const noMinify = file.opts.noMinify;
112 +
113 + if (!noMinify && path.get('callee').isIdentifier({name: 'Error'})) {
114 + CallOrNewExpression(path, file);
115 + return;
116 + }
117 +
118 if (path.get('callee').isIdentifier({name: 'invariant'})) {
119 // Turns this code:
120 //
@@ -40,7 +134,7 @@ module.exports = function(babel) {
134 // string) that references a verbose error message. The mapping is
135 // stored in `scripts/error-codes/codes.json`.
136 const condition = node.arguments[0];
43 - const errorMsgLiteral = evalToString(node.arguments[1]);
137 + const errorMsgLiteral = evalStringConcat(node.arguments[1]);
138 const errorMsgExpressions = Array.from(node.arguments.slice(2));
139 const errorMsgQuasis = errorMsgLiteral
140 .split('%s')
@@ -81,12 +175,6 @@ module.exports = function(babel) {
175 return;
176 }
177
84 - // Avoid caching because we write it as we go.
85 - const existingErrorMap = JSON.parse(
86 - fs.readFileSync(__dirname + '/codes.json', 'utf-8')
87 - );
88 - const errorMap = invertObject(existingErrorMap);
89 -
178 let prodErrorId = errorMap[errorMsgLiteral];
179
180 if (prodErrorId === undefined) {
@@ -117,7 +205,7 @@ module.exports = function(babel) {
205 }
206 prodErrorId = parseInt(prodErrorId, 10);
207
120 - // Import ReactErrorProd
208 + // Import formatProdErrorMessage
209 const formatProdErrorMessageIdentifier = helperModuleImports.addDefault(
210 path,
211 'shared/formatProdErrorMessage',
scripts/eslint-rules/__tests__/prod-error-codes-test.internal.js new
+77
@@ -0,0 +1,77 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @emails react-core
8 + */
9 +
10 +'use strict';
11 +
12 +const rule = require('../prod-error-codes');
13 +const {RuleTester} = require('eslint');
14 +const ruleTester = new RuleTester({
15 + parserOptions: {
16 + ecmaVersion: 2017,
17 + },
18 +});
19 +
20 +ruleTester.run('eslint-rules/prod-error-codes', rule, {
21 + valid: [
22 + 'arbitraryFunction(a, b)',
23 + 'Error(`Expected ${foo} target to be an array; got ${bar}`)',
24 + "Error('Expected ' + foo + ' target to be an array; got ' + bar)",
25 + 'Error(`Expected ${foo} target to ` + `be an array; got ${bar}`)',
26 + ],
27 + invalid: [
28 + {
29 + code: "Error('Not in error map')",
30 + errors: [
31 + {
32 + message:
33 + 'Error message does not have a corresponding production error ' +
34 + 'code. Add the following message to codes.json so it can be stripped from ' +
35 + 'the production builds:\n\n' +
36 + 'Not in error map',
37 + },
38 + ],
39 + },
40 + {
41 + code: "Error('Not in ' + 'error map')",
42 + errors: [
43 + {
44 + message:
45 + 'Error message does not have a corresponding production error ' +
46 + 'code. Add the following message to codes.json so it can be stripped from ' +
47 + 'the production builds:\n\n' +
48 + 'Not in error map',
49 + },
50 + ],
51 + },
52 + {
53 + code: 'Error(`Not in ` + `error map`)',
54 + errors: [
55 + {
56 + message:
57 + 'Error message does not have a corresponding production error ' +
58 + 'code. Add the following message to codes.json so it can be stripped from ' +
59 + 'the production builds:\n\n' +
60 + 'Not in error map',
61 + },
62 + ],
63 + },
64 + {
65 + code: "Error(`Not in ${'error'} map`)",
66 + errors: [
67 + {
68 + message:
69 + 'Error message does not have a corresponding production error ' +
70 + 'code. Add the following message to codes.json so it can be stripped from ' +
71 + 'the production builds:\n\n' +
72 + 'Not in %s map',
73 + },
74 + ],
75 + },
76 + ],
77 +});
scripts/eslint-rules/index.js
+1
@@ -6,6 +6,7 @@ module.exports = {
6 'no-to-warn-dev-within-to-throw': require('./no-to-warn-dev-within-to-throw'),
7 'warning-args': require('./warning-args'),
8 'invariant-args': require('./invariant-args'),
9 + 'prod-error-codes': require('./prod-error-codes'),
10 'no-production-logging': require('./no-production-logging'),
11 'no-cross-fork-imports': require('./no-cross-fork-imports'),
12 'no-cross-fork-types': require('./no-cross-fork-types'),
scripts/eslint-rules/prod-error-codes.js new
+79
@@ -0,0 +1,79 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @emails react-core
8 + */
9 +
10 +'use strict';
11 +
12 +const fs = require('fs');
13 +const path = require('path');
14 +const errorMap = JSON.parse(
15 + fs.readFileSync(path.resolve(__dirname, '../error-codes/codes.json'))
16 +);
17 +const errorMessages = new Set();
18 +Object.keys(errorMap).forEach(key => errorMessages.add(errorMap[key]));
19 +
20 +function nodeToErrorTemplate(node) {
21 + if (node.type === 'Literal' && typeof node.value === 'string') {
22 + return node.value;
23 + } else if (node.type === 'BinaryExpression' && node.operator === '+') {
24 + const l = nodeToErrorTemplate(node.left);
25 + const r = nodeToErrorTemplate(node.right);
26 + return l + r;
27 + } else if (node.type === 'TemplateLiteral') {
28 + let elements = [];
29 + for (let i = 0; i < node.quasis.length; i++) {
30 + const elementNode = node.quasis[i];
31 + if (elementNode.type !== 'TemplateElement') {
32 + throw new Error('Unsupported type ' + node.type);
33 + }
34 + elements.push(elementNode.value.cooked);
35 + }
36 + return elements.join('%s');
37 + } else {
38 + return '%s';
39 + }
40 +}
41 +
42 +module.exports = {
43 + meta: {
44 + schema: [],
45 + },
46 + create(context) {
47 + function ErrorCallExpression(node) {
48 + const errorMessageNode = node.arguments[0];
49 + if (errorMessageNode === undefined) {
50 + return;
51 + }
52 + const errorMessage = nodeToErrorTemplate(errorMessageNode);
53 + if (errorMessages.has(errorMessage)) {
54 + return;
55 + }
56 + context.report({
57 + node,
58 + message:
59 + 'Error message does not have a corresponding production error code. Add ' +
60 + 'the following message to codes.json so it can be stripped ' +
61 + 'from the production builds:\n\n' +
62 + errorMessage,
63 + });
64 + }
65 +
66 + return {
67 + NewExpression(node) {
68 + if (node.callee.type === 'Identifier' && node.callee.name === 'Error') {
69 + ErrorCallExpression(node);
70 + }
71 + },
72 + CallExpression(node) {
73 + if (node.callee.type === 'Identifier' && node.callee.name === 'Error') {
74 + ErrorCallExpression(node);
75 + }
76 + },
77 + };
78 + },
79 +};
scripts/print-warnings/print-warnings.js
+2 -2
@@ -12,7 +12,7 @@ const through = require('through2');
12 const traverse = require('@babel/traverse').default;
13 const gs = require('glob-stream');
14
15 -const evalToString = require('../shared/evalToString');
15 +const {evalStringConcat} = require('../shared/evalToString');
16
17 const parserOptions = {
18 sourceType: 'module',
@@ -64,7 +64,7 @@ function transform(file, enc, cb) {
64 // warning messages can be concatenated (`+`) at runtime, so here's
65 // a trivial partial evaluator that interprets the literal value
66 try {
67 - const warningMsgLiteral = evalToString(node.arguments[0]);
67 + const warningMsgLiteral = evalStringConcat(node.arguments[0]);
68 warnings.add(JSON.stringify(warningMsgLiteral));
69 } catch (error) {
70 console.error(
scripts/rollup/build.js
+7 -10
@@ -149,7 +149,8 @@ function getBabelConfig(
149 bundleType,
150 packageName,
151 externals,
152 - isDevelopment
152 + isDevelopment,
153 + bundle
154 ) {
155 const canAccessReactObject =
156 packageName === 'react' || externals.indexOf('react') !== -1;
@@ -179,12 +180,6 @@ function getBabelConfig(
180 case FB_WWW_DEV:
181 case FB_WWW_PROD:
182 case FB_WWW_PROFILING:
182 - return Object.assign({}, options, {
183 - plugins: options.plugins.concat([
184 - // Minify invariant messages
185 - require('../error-codes/transform-error-messages'),
186 - ]),
187 - });
183 case RN_OSS_DEV:
184 case RN_OSS_PROD:
185 case RN_OSS_PROFILING:
@@ -195,8 +190,9 @@ function getBabelConfig(
190 plugins: options.plugins.concat([
191 [
192 require('../error-codes/transform-error-messages'),
198 - // Preserve full error messages in React Native build
199 - {noMinify: true},
193 + // Controls whether to replace error messages with error codes
194 + // in production. By default, error messages are replaced.
195 + {noMinify: bundle.minifyWithProdErrorCodes === false},
196 ],
197 ]),
198 });
@@ -390,7 +386,8 @@ function getPlugins(
386 bundleType,
387 packageName,
388 externals,
393 - !isProduction
389 + !isProduction,
390 + bundle
391 )
392 ),
393 // Remove 'use strict' from individual source files.
scripts/rollup/bundles.js
+63
@@ -79,6 +79,7 @@ const bundles = [
79 moduleType: ISOMORPHIC,
80 entry: 'react',
81 global: 'React',
82 + minifyWithProdErrorCodes: false,
83 externals: ['ReactNativeInternalFeatureFlags'],
84 },
85
@@ -88,6 +89,7 @@ const bundles = [
89 moduleType: ISOMORPHIC,
90 entry: 'react/unstable-shared-subset',
91 global: 'React',
92 + minifyWithProdErrorCodes: true,
93 externals: [],
94 },
95
@@ -105,6 +107,7 @@ const bundles = [
107 moduleType: ISOMORPHIC,
108 entry: 'react/jsx-runtime',
109 global: 'JSXRuntime',
110 + minifyWithProdErrorCodes: true,
111 externals: ['react', 'ReactNativeInternalFeatureFlags'],
112 },
113
@@ -124,6 +127,7 @@ const bundles = [
127 moduleType: ISOMORPHIC,
128 entry: 'react/jsx-dev-runtime',
129 global: 'JSXDEVRuntime',
130 + minifyWithProdErrorCodes: false,
131 externals: ['react', 'ReactNativeInternalFeatureFlags'],
132 },
133
@@ -133,6 +137,7 @@ const bundles = [
137 moduleType: ISOMORPHIC,
138 entry: 'react-fetch/index.browser',
139 global: 'ReactFetch',
140 + minifyWithProdErrorCodes: true,
141 externals: ['react'],
142 },
143
@@ -142,6 +147,7 @@ const bundles = [
147 moduleType: ISOMORPHIC,
148 entry: 'react-fetch/index.node',
149 global: 'ReactFetch',
150 + minifyWithProdErrorCodes: false,
151 externals: ['react', 'http', 'https'],
152 },
153
@@ -151,6 +157,7 @@ const bundles = [
157 moduleType: ISOMORPHIC,
158 entry: 'react-fs/index.browser.server',
159 global: 'ReactFilesystem',
160 + minifyWithProdErrorCodes: true,
161 externals: [],
162 },
163
@@ -160,6 +167,7 @@ const bundles = [
167 moduleType: ISOMORPHIC,
168 entry: 'react-fs/index.node.server',
169 global: 'ReactFilesystem',
170 + minifyWithProdErrorCodes: false,
171 externals: ['react', 'fs/promises', 'path'],
172 },
173
@@ -169,6 +177,7 @@ const bundles = [
177 moduleType: ISOMORPHIC,
178 entry: 'react-pg/index.browser.server',
179 global: 'ReactPostgres',
180 + minifyWithProdErrorCodes: true,
181 externals: [],
182 },
183
@@ -178,6 +187,7 @@ const bundles = [
187 moduleType: ISOMORPHIC,
188 entry: 'react-pg/index.node.server',
189 global: 'ReactPostgres',
190 + minifyWithProdErrorCodes: false,
191 externals: ['react', 'pg'],
192 },
193
@@ -197,6 +207,7 @@ const bundles = [
207 moduleType: RENDERER,
208 entry: 'react-dom',
209 global: 'ReactDOM',
210 + minifyWithProdErrorCodes: true,
211 externals: ['react'],
212 },
213
@@ -207,6 +218,7 @@ const bundles = [
218 entry: 'react-dom',
219 global: 'ReactDOMForked',
220 enableNewReconciler: true,
221 + minifyWithProdErrorCodes: true,
222 externals: ['react'],
223 },
224
@@ -216,15 +228,18 @@ const bundles = [
228 bundleTypes: [FB_WWW_DEV, NODE_DEV, NODE_PROD, UMD_DEV, UMD_PROD],
229 entry: 'react-dom/test-utils',
230 global: 'ReactTestUtils',
231 + minifyWithProdErrorCodes: false,
232 externals: ['react', 'react-dom'],
233 },
234
235 + // TODO: We don't use this bundle anymore
236 /******* React DOM - www - Testing *******/
237 {
238 moduleType: RENDERER,
239 bundleTypes: [FB_WWW_DEV, FB_WWW_PROD],
240 entry: 'react-dom/testing',
241 global: 'ReactDOMTesting',
242 + minifyWithProdErrorCodes: true,
243 externals: ['react'],
244 },
245
@@ -237,6 +252,7 @@ const bundles = [
252 entry: 'react-dom/src/server/ReactDOMLegacyServerBrowser',
253 name: 'react-dom-server-legacy.browser',
254 global: 'ReactDOMServer',
255 + minifyWithProdErrorCodes: true,
256 externals: ['react'],
257 babel: opts =>
258 Object.assign({}, opts, {
@@ -251,6 +267,7 @@ const bundles = [
267 entry: 'react-dom/src/server/ReactDOMLegacyServerNode',
268 name: 'react-dom-server-legacy.node',
269 externals: ['react', 'stream'],
270 + minifyWithProdErrorCodes: false,
271 babel: opts =>
272 Object.assign({}, opts, {
273 plugins: opts.plugins.concat([
@@ -266,6 +283,7 @@ const bundles = [
283 entry: 'react-dom/src/server/ReactDOMFizzServerBrowser',
284 name: 'react-dom-server.browser',
285 global: 'ReactDOMServer',
286 + minifyWithProdErrorCodes: true,
287 externals: ['react'],
288 },
289 {
@@ -274,6 +292,7 @@ const bundles = [
292 entry: 'react-dom/src/server/ReactDOMFizzServerNode',
293 name: 'react-dom-server.node',
294 global: 'ReactDOMServer',
295 + minifyWithProdErrorCodes: false,
296 externals: ['react'],
297 },
298 {
@@ -281,6 +300,7 @@ const bundles = [
300 moduleType: RENDERER,
301 entry: 'react-server-dom-relay/src/ReactDOMServerFB',
302 global: 'ReactDOMServer',
303 + minifyWithProdErrorCodes: false,
304 externals: ['react'],
305 },
306
@@ -290,6 +310,7 @@ const bundles = [
310 moduleType: RENDERER,
311 entry: 'react-server-dom-webpack/writer.browser.server',
312 global: 'ReactServerDOMWriter',
313 + minifyWithProdErrorCodes: false,
314 externals: ['react'],
315 },
316 {
@@ -297,6 +318,7 @@ const bundles = [
318 moduleType: RENDERER,
319 entry: 'react-server-dom-webpack/writer.node.server',
320 global: 'ReactServerDOMWriter',
321 + minifyWithProdErrorCodes: false,
322 externals: ['react'],
323 },
324
@@ -306,6 +328,7 @@ const bundles = [
328 moduleType: RENDERER,
329 entry: 'react-server-dom-webpack',
330 global: 'ReactServerDOMReader',
331 + minifyWithProdErrorCodes: true,
332 externals: ['react'],
333 },
334
@@ -315,6 +338,7 @@ const bundles = [
338 moduleType: RENDERER_UTILS,
339 entry: 'react-server-dom-webpack/plugin',
340 global: 'ReactServerWebpackPlugin',
341 + minifyWithProdErrorCodes: false,
342 externals: ['fs', 'path', 'url', 'neo-async'],
343 },
344
@@ -324,6 +348,7 @@ const bundles = [
348 moduleType: RENDERER_UTILS,
349 entry: 'react-server-dom-webpack/node-loader',
350 global: 'ReactServerWebpackNodeLoader',
351 + minifyWithProdErrorCodes: false,
352 externals: ['acorn'],
353 },
354
@@ -333,6 +358,7 @@ const bundles = [
358 moduleType: RENDERER_UTILS,
359 entry: 'react-server-dom-webpack/node-register',
360 global: 'ReactFlightWebpackNodeRegister',
361 + minifyWithProdErrorCodes: false,
362 externals: ['url', 'module'],
363 },
364
@@ -342,6 +368,7 @@ const bundles = [
368 moduleType: RENDERER,
369 entry: 'react-server-dom-relay/server',
370 global: 'ReactFlightDOMRelayServer', // TODO: Rename to Writer
371 + minifyWithProdErrorCodes: false,
372 externals: [
373 'react',
374 'ReactFlightDOMRelayServerIntegration',
@@ -355,6 +382,7 @@ const bundles = [
382 moduleType: RENDERER,
383 entry: 'react-server-dom-relay',
384 global: 'ReactFlightDOMRelayClient', // TODO: Rename to Reader
385 + minifyWithProdErrorCodes: true,
386 externals: [
387 'react',
388 'ReactFlightDOMRelayClientIntegration',
@@ -368,6 +396,7 @@ const bundles = [
396 moduleType: RENDERER,
397 entry: 'react-server-native-relay/server',
398 global: 'ReactFlightNativeRelayServer', // TODO: Rename to Writer
399 + minifyWithProdErrorCodes: false,
400 externals: [
401 'react',
402 'ReactFlightNativeRelayServerIntegration',
@@ -382,6 +411,7 @@ const bundles = [
411 moduleType: RENDERER,
412 entry: 'react-server-native-relay',
413 global: 'ReactFlightNativeRelayClient', // TODO: Rename to Reader
414 + minifyWithProdErrorCodes: true,
415 externals: [
416 'react',
417 'ReactFlightNativeRelayClientIntegration',
@@ -396,6 +426,7 @@ const bundles = [
426 moduleType: RENDERER_UTILS,
427 entry: 'react-suspense-test-utils',
428 global: 'ReactSuspenseTestUtils',
429 + minifyWithProdErrorCodes: false,
430 externals: ['react'],
431 },
432
@@ -413,6 +444,7 @@ const bundles = [
444 entry: 'react-art',
445 global: 'ReactART',
446 externals: ['react'],
447 + minifyWithProdErrorCodes: true,
448 babel: opts =>
449 Object.assign({}, opts, {
450 // Include JSX
@@ -435,6 +467,7 @@ const bundles = [
467 entry: 'react-native-renderer',
468 global: 'ReactNativeRenderer',
469 externals: ['react-native', 'ReactNativeInternalFeatureFlags'],
470 + minifyWithProdErrorCodes: false,
471 babel: opts =>
472 Object.assign({}, opts, {
473 plugins: opts.plugins.concat([
@@ -448,6 +481,7 @@ const bundles = [
481 entry: 'react-native-renderer',
482 global: 'ReactNativeRenderer',
483 externals: ['react-native'],
484 + minifyWithProdErrorCodes: false,
485 babel: opts =>
486 Object.assign({}, opts, {
487 plugins: opts.plugins.concat([
@@ -465,6 +499,7 @@ const bundles = [
499 entry: 'react-native-renderer/fabric',
500 global: 'ReactFabric',
501 externals: ['react-native', 'ReactNativeInternalFeatureFlags'],
502 + minifyWithProdErrorCodes: false,
503 babel: opts =>
504 Object.assign({}, opts, {
505 plugins: opts.plugins.concat([
@@ -478,6 +513,7 @@ const bundles = [
513 entry: 'react-native-renderer/fabric',
514 global: 'ReactFabric',
515 externals: ['react-native'],
516 + minifyWithProdErrorCodes: false,
517 babel: opts =>
518 Object.assign({}, opts, {
519 plugins: opts.plugins.concat([
@@ -507,6 +543,7 @@ const bundles = [
543 'scheduler/unstable_mock',
544 'ReactNativeInternalFeatureFlags',
545 ],
546 + minifyWithProdErrorCodes: false,
547 babel: opts =>
548 Object.assign({}, opts, {
549 plugins: opts.plugins.concat([
@@ -521,6 +558,7 @@ const bundles = [
558 moduleType: RENDERER,
559 entry: 'react-noop-renderer',
560 global: 'ReactNoopRenderer',
561 + minifyWithProdErrorCodes: true,
562 externals: ['react', 'scheduler', 'scheduler/unstable_mock', 'expect'],
563 },
564
@@ -530,6 +568,7 @@ const bundles = [
568 moduleType: RENDERER,
569 entry: 'react-noop-renderer/persistent',
570 global: 'ReactNoopRendererPersistent',
571 + minifyWithProdErrorCodes: true,
572 externals: ['react', 'scheduler', 'expect'],
573 },
574
@@ -539,6 +578,7 @@ const bundles = [
578 moduleType: RENDERER,
579 entry: 'react-noop-renderer/server',
580 global: 'ReactNoopRendererServer',
581 + minifyWithProdErrorCodes: true,
582 externals: ['react', 'scheduler', 'expect'],
583 },
584
@@ -548,6 +588,7 @@ const bundles = [
588 moduleType: RENDERER,
589 entry: 'react-noop-renderer/flight-server',
590 global: 'ReactNoopFlightServer',
591 + minifyWithProdErrorCodes: false,
592 externals: [
593 'react',
594 'scheduler',
@@ -562,6 +603,7 @@ const bundles = [
603 moduleType: RENDERER,
604 entry: 'react-noop-renderer/flight-client',
605 global: 'ReactNoopFlightClient',
606 + minifyWithProdErrorCodes: false,
607 externals: [
608 'react',
609 'scheduler',
@@ -576,6 +618,7 @@ const bundles = [
618 moduleType: RECONCILER,
619 entry: 'react-reconciler',
620 global: 'ReactReconciler',
621 + minifyWithProdErrorCodes: true,
622 externals: ['react'],
623 },
624
@@ -585,6 +628,7 @@ const bundles = [
628 moduleType: RECONCILER,
629 entry: 'react-server',
630 global: 'ReactServer',
631 + minifyWithProdErrorCodes: false,
632 externals: ['react'],
633 },
634
@@ -594,6 +638,7 @@ const bundles = [
638 moduleType: RECONCILER,
639 entry: 'react-server/flight',
640 global: 'ReactFlightServer',
641 + minifyWithProdErrorCodes: false,
642 externals: ['react'],
643 },
644
@@ -603,6 +648,7 @@ const bundles = [
648 moduleType: RECONCILER,
649 entry: 'react-client/flight',
650 global: 'ReactFlightClient',
651 + minifyWithProdErrorCodes: true,
652 externals: ['react'],
653 },
654
@@ -612,6 +658,7 @@ const bundles = [
658 bundleTypes: [NODE_DEV, NODE_PROD],
659 entry: 'react-reconciler/reflection',
660 global: 'ReactFiberTreeReflection',
661 + minifyWithProdErrorCodes: true,
662 externals: [],
663 },
664
@@ -621,6 +668,7 @@ const bundles = [
668 bundleTypes: [NODE_DEV, NODE_PROD],
669 entry: 'react-reconciler/constants',
670 global: 'ReactReconcilerConstants',
671 + minifyWithProdErrorCodes: true,
672 externals: [],
673 },
674
@@ -637,6 +685,7 @@ const bundles = [
685 moduleType: ISOMORPHIC,
686 entry: 'react-is',
687 global: 'ReactIs',
688 + minifyWithProdErrorCodes: true,
689 externals: [],
690 },
691
@@ -646,6 +695,7 @@ const bundles = [
695 moduleType: ISOMORPHIC,
696 entry: 'react-debug-tools',
697 global: 'ReactDebugTools',
698 + minifyWithProdErrorCodes: false,
699 externals: [],
700 },
701
@@ -657,6 +707,7 @@ const bundles = [
707 moduleType: ISOMORPHIC,
708 entry: 'react-cache',
709 global: 'ReactCacheOld',
710 + minifyWithProdErrorCodes: false,
711 externals: ['react', 'scheduler'],
712 },
713
@@ -667,6 +718,7 @@ const bundles = [
718 entry: 'create-subscription',
719 global: 'createSubscription',
720 externals: ['react'],
721 + minifyWithProdErrorCodes: true,
722 babel: opts =>
723 Object.assign({}, opts, {
724 plugins: opts.plugins.concat([
@@ -681,6 +733,7 @@ const bundles = [
733 moduleType: ISOMORPHIC,
734 entry: 'use-subscription',
735 global: 'useSubscription',
736 + minifyWithProdErrorCodes: true,
737 externals: ['react'],
738 },
739
@@ -690,6 +743,7 @@ const bundles = [
743 moduleType: ISOMORPHIC,
744 entry: 'use-sync-external-store',
745 global: 'useSyncExternalStore',
746 + minifyWithProdErrorCodes: true,
747 externals: ['react'],
748 },
749
@@ -699,6 +753,7 @@ const bundles = [
753 moduleType: ISOMORPHIC,
754 entry: 'use-sync-external-store/extra',
755 global: 'useSyncExternalStoreExtra',
756 + minifyWithProdErrorCodes: true,
757 externals: ['react', 'use-sync-external-store'],
758 },
759
@@ -708,6 +763,7 @@ const bundles = [
763 moduleType: ISOMORPHIC,
764 entry: 'use-sync-external-store/index.native',
765 global: 'useSyncExternalStoreNative',
766 + minifyWithProdErrorCodes: true,
767 externals: ['react', 'ReactNativeInternalFeatureFlags'],
768 },
769
@@ -726,6 +782,7 @@ const bundles = [
782 moduleType: ISOMORPHIC,
783 entry: 'scheduler',
784 global: 'Scheduler',
785 + minifyWithProdErrorCodes: true,
786 externals: ['ReactNativeInternalFeatureFlags'],
787 },
788
@@ -744,6 +801,7 @@ const bundles = [
801 moduleType: ISOMORPHIC,
802 entry: 'scheduler/unstable_mock',
803 global: 'SchedulerMock',
804 + minifyWithProdErrorCodes: false,
805 externals: ['ReactNativeInternalFeatureFlags'],
806 },
807
@@ -759,6 +817,7 @@ const bundles = [
817 moduleType: ISOMORPHIC,
818 entry: 'scheduler/unstable_post_task',
819 global: 'SchedulerPostTask',
820 + minifyWithProdErrorCodes: true,
821 externals: [],
822 },
823
@@ -768,6 +827,7 @@ const bundles = [
827 moduleType: ISOMORPHIC,
828 entry: 'jest-react',
829 global: 'JestReact',
830 + minifyWithProdErrorCodes: false,
831 externals: ['react', 'scheduler', 'scheduler/unstable_mock'],
832 },
833
@@ -781,6 +841,7 @@ const bundles = [
841 moduleType: ISOMORPHIC,
842 entry: 'eslint-plugin-react-hooks',
843 global: 'ESLintPluginReactHooks',
844 + minifyWithProdErrorCodes: false,
845 externals: [],
846 },
847
@@ -790,6 +851,7 @@ const bundles = [
851 moduleType: ISOMORPHIC,
852 entry: 'react-refresh/babel',
853 global: 'ReactFreshBabelPlugin',
854 + minifyWithProdErrorCodes: false,
855 externals: [],
856 },
857 {
@@ -797,6 +859,7 @@ const bundles = [
859 moduleType: ISOMORPHIC,
860 entry: 'react-refresh/runtime',
861 global: 'ReactFreshRuntime',
862 + minifyWithProdErrorCodes: false,
863 externals: [],
864 },
865 ];
scripts/shared/__tests__/evalToString-test.js
+2 -2
@@ -6,12 +6,12 @@
6 */
7 'use strict';
8
9 -const evalToString = require('../evalToString');
9 +const {evalStringConcat} = require('../evalToString');
10 const parser = require('@babel/parser');
11
12 const parse = source => parser.parse(`(${source});`).program.body[0].expression; // quick way to get an exp node
13
14 -const parseAndEval = source => evalToString(parse(source));
14 +const parseAndEval = source => evalStringConcat(parse(source));
15
16 describe('evalToString', () => {
17 it('should support StringLiteral', () => {
scripts/shared/evalToString.js
+37 -3
@@ -8,7 +8,7 @@
8 */
9 'use strict';
10
11 -function evalToString(ast /* : Object */) /* : string */ {
11 +function evalStringConcat(ast /* : Object */) /* : string */ {
12 switch (ast.type) {
13 case 'StringLiteral':
14 case 'Literal': // ESLint
@@ -17,10 +17,44 @@ function evalToString(ast /* : Object */) /* : string */ {
17 if (ast.operator !== '+') {
18 throw new Error('Unsupported binary operator ' + ast.operator);
19 }
20 - return evalToString(ast.left) + evalToString(ast.right);
20 + return evalStringConcat(ast.left) + evalStringConcat(ast.right);
21 default:
22 throw new Error('Unsupported type ' + ast.type);
23 }
24 }
25 +exports.evalStringConcat = evalStringConcat;
26
26 -module.exports = evalToString;
27 +function evalStringAndTemplateConcat(
28 + ast /* : Object */,
29 + args /* : Array<mixed> */
30 +) /* : string */ {
31 + switch (ast.type) {
32 + case 'StringLiteral':
33 + return ast.value;
34 + case 'BinaryExpression': // `+`
35 + if (ast.operator !== '+') {
36 + throw new Error('Unsupported binary operator ' + ast.operator);
37 + }
38 + return (
39 + evalStringAndTemplateConcat(ast.left, args) +
40 + evalStringAndTemplateConcat(ast.right, args)
41 + );
42 + case 'TemplateLiteral': {
43 + let elements = [];
44 + for (let i = 0; i < ast.quasis.length; i++) {
45 + const elementNode = ast.quasis[i];
46 + if (elementNode.type !== 'TemplateElement') {
47 + throw new Error('Unsupported type ' + ast.type);
48 + }
49 + elements.push(elementNode.value.cooked);
50 + }
51 + args.push(...ast.expressions);
52 + return elements.join('%s');
53 + }
54 + default:
55 + // Anything that's not a string is interpreted as an argument.
56 + args.push(ast);
57 + return '%s';
58 + }
59 +}
60 +exports.evalStringAndTemplateConcat = evalStringAndTemplateConcat;
yarn.lock
-1
@@ -6442,7 +6442,6 @@ eslint-plugin-no-unsanitized@3.1.2:
6442
6443 "eslint-plugin-react-internal@link:./scripts/eslint-rules":
6444 version "0.0.0"
6445 - uid ""
6445
6446 eslint-plugin-react@^6.7.1:
6447 version "6.10.3"