Fork ReactFiberScheduler with feature flag
Adds a feature flag `enableNewScheduler` that toggles between two implementations of ReactFiberScheduler. This will let us land changes in master while preserving the ability to quickly rollback. Ideally this will be a short-lived fork. Once we've tested the new scheduler for a week or so without issues, we will get rid of it. Until then, we'll need to maintain two parallel implementations and run tests against both of them. We rarely land changes to ReactFiberScheduler, so I don't expect this will be a huge burden. This commit does not implement anything new. The flag is still off and tests run against the existing implementation. Use `yarn test-new-scheduler` to run tests against the new one.
Andrew Clark committed
Mar 20, 2019 at 16:28 UTC
b1a56abd6aec4379c2fc9400b413f6f42d0c9b1f
19 files changed
+202
-9
package.json
+1
@@ -102,6 +102,7 @@
102
"test": "cross-env NODE_ENV=development jest --config ./scripts/jest/config.source.js",
103
"test-persistent": "cross-env NODE_ENV=development jest --config ./scripts/jest/config.source-persistent.js",
104
"test-fire": "cross-env NODE_ENV=development jest --config ./scripts/jest/config.source-fire.js",
105
+ "test-new-scheduler": "cross-env NODE_ENV=development jest --config ./scripts/jest/config.source-new-scheduler.js",
106
"test-prod": "cross-env NODE_ENV=production jest --config ./scripts/jest/config.source.js",
107
"test-fire-prod": "cross-env NODE_ENV=production jest --config ./scripts/jest/config.source-fire.js",
108
"test-prod-build": "yarn test-build-prod",
packages/react-reconciler/src/ReactFiberBeginWork.js
+1
-1
@@ -144,7 +144,7 @@ import {
144
createWorkInProgress,
145
isSimpleFunctionComponent,
146
} from './ReactFiber';
147
-import {requestCurrentTime, retryTimedOutBoundary} from './ReactFiberScheduler.old';
147
+import {requestCurrentTime, retryTimedOutBoundary} from './ReactFiberScheduler';
148
149
const ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;
150
packages/react-reconciler/src/ReactFiberClassComponent.js
+1
-1
@@ -53,7 +53,7 @@ import {
53
computeExpirationForFiber,
54
scheduleWork,
55
flushPassiveEffects,
56
-} from './ReactFiberScheduler.old';
56
+} from './ReactFiberScheduler';
57
58
const fakeInternalInstance = {};
59
const isArray = Array.isArray;
packages/react-reconciler/src/ReactFiberCommitWork.js
+2
-2
@@ -21,7 +21,7 @@ import type {ExpirationTime} from './ReactFiberExpirationTime';
21
import type {CapturedValue, CapturedError} from './ReactCapturedValue';
22
import type {SuspenseState} from './ReactFiberSuspenseComponent';
23
import type {FunctionComponentUpdateQueue} from './ReactFiberHooks';
24
-import type {Thenable} from './ReactFiberScheduler.old';
24
+import type {Thenable} from './ReactFiberScheduler';
25
26
import {unstable_wrap as Schedule_tracing_wrap} from 'scheduler/tracing';
27
import {
@@ -95,7 +95,7 @@ import {
95
captureCommitPhaseError,
96
requestCurrentTime,
97
resolveRetryThenable,
98
-} from './ReactFiberScheduler.old';
98
+} from './ReactFiberScheduler';
99
import {
100
NoEffect as NoHookEffect,
101
UnmountSnapshot,
packages/react-reconciler/src/ReactFiberHooks.js
+1
-1
@@ -34,7 +34,7 @@ import {
34
computeExpirationForFiber,
35
flushPassiveEffects,
36
requestCurrentTime,
37
-} from './ReactFiberScheduler.old';
37
+} from './ReactFiberScheduler';
38
39
import invariant from 'shared/invariant';
40
import warning from 'shared/warning';
packages/react-reconciler/src/ReactFiberReconciler.js
+1
-1
@@ -54,7 +54,7 @@ import {
54
interactiveUpdates,
55
flushInteractiveUpdates,
56
flushPassiveEffects,
57
-} from './ReactFiberScheduler.old';
57
+} from './ReactFiberScheduler';
58
import {createUpdate, enqueueUpdate} from './ReactUpdateQueue';
59
import ReactFiberInstrumentation from './ReactFiberInstrumentation';
60
import {
packages/react-reconciler/src/ReactFiberRoot.js
+1
-1
@@ -10,7 +10,7 @@
10
import type {Fiber} from './ReactFiber';
11
import type {ExpirationTime} from './ReactFiberExpirationTime';
12
import type {TimeoutHandle, NoTimeout} from './ReactFiberHostConfig';
13
-import type {Thenable} from './ReactFiberScheduler.old';
13
+import type {Thenable} from './ReactFiberScheduler';
14
import type {Interaction} from 'scheduler/src/Tracing';
15
16
import {noTimeout} from './ReactFiberHostConfig';
packages/react-reconciler/src/ReactFiberScheduler.js
new
+124
@@ -0,0 +1,124 @@
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
+ * @flow
8
+ */
9
+
10
+import {enableNewScheduler} from 'shared/ReactFeatureFlags';
11
+
12
+import {
13
+ requestCurrentTime as requestCurrentTime_old,
14
+ computeExpirationForFiber as computeExpirationForFiber_old,
15
+ captureCommitPhaseError as captureCommitPhaseError_old,
16
+ onUncaughtError as onUncaughtError_old,
17
+ renderDidSuspend as renderDidSuspend_old,
18
+ renderDidError as renderDidError_old,
19
+ pingSuspendedRoot as pingSuspendedRoot_old,
20
+ retryTimedOutBoundary as retryTimedOutBoundary_old,
21
+ resolveRetryThenable as resolveRetryThenable_old,
22
+ markLegacyErrorBoundaryAsFailed as markLegacyErrorBoundaryAsFailed_old,
23
+ isAlreadyFailedLegacyErrorBoundary as isAlreadyFailedLegacyErrorBoundary_old,
24
+ scheduleWork as scheduleWork_old,
25
+ requestWork as requestWork_old,
26
+ flushRoot as flushRoot_old,
27
+ batchedUpdates as batchedUpdates_old,
28
+ unbatchedUpdates as unbatchedUpdates_old,
29
+ flushSync as flushSync_old,
30
+ flushControlled as flushControlled_old,
31
+ deferredUpdates as deferredUpdates_old,
32
+ syncUpdates as syncUpdates_old,
33
+ interactiveUpdates as interactiveUpdates_old,
34
+ flushInteractiveUpdates as flushInteractiveUpdates_old,
35
+ computeUniqueAsyncExpiration as computeUniqueAsyncExpiration_old,
36
+ flushPassiveEffects as flushPassiveEffects_old,
37
+ warnIfNotCurrentlyBatchingInDev as warnIfNotCurrentlyBatchingInDev_old,
38
+} from './ReactFiberScheduler.old';
39
+
40
+import {
41
+ requestCurrentTime as requestCurrentTime_new,
42
+ computeExpirationForFiber as computeExpirationForFiber_new,
43
+ captureCommitPhaseError as captureCommitPhaseError_new,
44
+ onUncaughtError as onUncaughtError_new,
45
+ renderDidSuspend as renderDidSuspend_new,
46
+ renderDidError as renderDidError_new,
47
+ pingSuspendedRoot as pingSuspendedRoot_new,
48
+ retryTimedOutBoundary as retryTimedOutBoundary_new,
49
+ resolveRetryThenable as resolveRetryThenable_new,
50
+ markLegacyErrorBoundaryAsFailed as markLegacyErrorBoundaryAsFailed_new,
51
+ isAlreadyFailedLegacyErrorBoundary as isAlreadyFailedLegacyErrorBoundary_new,
52
+ scheduleWork as scheduleWork_new,
53
+ requestWork as requestWork_new,
54
+ flushRoot as flushRoot_new,
55
+ batchedUpdates as batchedUpdates_new,
56
+ unbatchedUpdates as unbatchedUpdates_new,
57
+ flushSync as flushSync_new,
58
+ flushControlled as flushControlled_new,
59
+ deferredUpdates as deferredUpdates_new,
60
+ syncUpdates as syncUpdates_new,
61
+ interactiveUpdates as interactiveUpdates_new,
62
+ flushInteractiveUpdates as flushInteractiveUpdates_new,
63
+ computeUniqueAsyncExpiration as computeUniqueAsyncExpiration_new,
64
+ flushPassiveEffects as flushPassiveEffects_new,
65
+ warnIfNotCurrentlyBatchingInDev as warnIfNotCurrentlyBatchingInDev_new,
66
+} from './ReactFiberScheduler.new';
67
+
68
+export let requestCurrentTime = requestCurrentTime_old;
69
+export let computeExpirationForFiber = computeExpirationForFiber_old;
70
+export let captureCommitPhaseError = captureCommitPhaseError_old;
71
+export let onUncaughtError = onUncaughtError_old;
72
+export let renderDidSuspend = renderDidSuspend_old;
73
+export let renderDidError = renderDidError_old;
74
+export let pingSuspendedRoot = pingSuspendedRoot_old;
75
+export let retryTimedOutBoundary = retryTimedOutBoundary_old;
76
+export let resolveRetryThenable = resolveRetryThenable_old;
77
+export let markLegacyErrorBoundaryAsFailed = markLegacyErrorBoundaryAsFailed_old;
78
+export let isAlreadyFailedLegacyErrorBoundary = isAlreadyFailedLegacyErrorBoundary_old;
79
+export let scheduleWork = scheduleWork_old;
80
+export let requestWork = requestWork_old;
81
+export let flushRoot = flushRoot_old;
82
+export let batchedUpdates = batchedUpdates_old;
83
+export let unbatchedUpdates = unbatchedUpdates_old;
84
+export let flushSync = flushSync_old;
85
+export let flushControlled = flushControlled_old;
86
+export let deferredUpdates = deferredUpdates_old;
87
+export let syncUpdates = syncUpdates_old;
88
+export let interactiveUpdates = interactiveUpdates_old;
89
+export let flushInteractiveUpdates = flushInteractiveUpdates_old;
90
+export let computeUniqueAsyncExpiration = computeUniqueAsyncExpiration_old;
91
+export let flushPassiveEffects = flushPassiveEffects_old;
92
+export let warnIfNotCurrentlyBatchingInDev = warnIfNotCurrentlyBatchingInDev_old;
93
+
94
+if (enableNewScheduler) {
95
+ requestCurrentTime = requestCurrentTime_new;
96
+ computeExpirationForFiber = computeExpirationForFiber_new;
97
+ captureCommitPhaseError = captureCommitPhaseError_new;
98
+ onUncaughtError = onUncaughtError_new;
99
+ renderDidSuspend = renderDidSuspend_new;
100
+ renderDidError = renderDidError_new;
101
+ pingSuspendedRoot = pingSuspendedRoot_new;
102
+ retryTimedOutBoundary = retryTimedOutBoundary_new;
103
+ resolveRetryThenable = resolveRetryThenable_new;
104
+ markLegacyErrorBoundaryAsFailed = markLegacyErrorBoundaryAsFailed_new;
105
+ isAlreadyFailedLegacyErrorBoundary = isAlreadyFailedLegacyErrorBoundary_new;
106
+ scheduleWork = scheduleWork_new;
107
+ requestWork = requestWork_new;
108
+ flushRoot = flushRoot_new;
109
+ batchedUpdates = batchedUpdates_new;
110
+ unbatchedUpdates = unbatchedUpdates_new;
111
+ flushSync = flushSync_new;
112
+ flushControlled = flushControlled_new;
113
+ deferredUpdates = deferredUpdates_new;
114
+ syncUpdates = syncUpdates_new;
115
+ interactiveUpdates = interactiveUpdates_new;
116
+ flushInteractiveUpdates = flushInteractiveUpdates_new;
117
+ computeUniqueAsyncExpiration = computeUniqueAsyncExpiration_new;
118
+ flushPassiveEffects = flushPassiveEffects_new;
119
+ warnIfNotCurrentlyBatchingInDev = warnIfNotCurrentlyBatchingInDev_new;
120
+}
121
+
122
+export type Thenable = {
123
+ then(resolve: () => mixed, reject?: () => mixed): mixed,
124
+};
packages/react-reconciler/src/ReactFiberScheduler.new.js
new
+36
@@ -0,0 +1,36 @@
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
+
8
+function notYetImplemented() {
9
+ throw new Error('Not yet implemented.');
10
+}
11
+
12
+export const requestCurrentTime = notYetImplemented;
13
+export const computeExpirationForFiber = notYetImplemented;
14
+export const captureCommitPhaseError = notYetImplemented;
15
+export const onUncaughtError = notYetImplemented;
16
+export const renderDidSuspend = notYetImplemented;
17
+export const renderDidError = notYetImplemented;
18
+export const pingSuspendedRoot = notYetImplemented;
19
+export const retryTimedOutBoundary = notYetImplemented;
20
+export const resolveRetryThenable = notYetImplemented;
21
+export const markLegacyErrorBoundaryAsFailed = notYetImplemented;
22
+export const isAlreadyFailedLegacyErrorBoundary = notYetImplemented;
23
+export const scheduleWork = notYetImplemented;
24
+export const requestWork = notYetImplemented;
25
+export const flushRoot = notYetImplemented;
26
+export const batchedUpdates = notYetImplemented;
27
+export const unbatchedUpdates = notYetImplemented;
28
+export const flushSync = notYetImplemented;
29
+export const flushControlled = notYetImplemented;
30
+export const deferredUpdates = notYetImplemented;
31
+export const syncUpdates = notYetImplemented;
32
+export const interactiveUpdates = notYetImplemented;
33
+export const flushInteractiveUpdates = notYetImplemented;
34
+export const computeUniqueAsyncExpiration = notYetImplemented;
35
+export const flushPassiveEffects = notYetImplemented;
36
+export const warnIfNotCurrentlyBatchingInDev = notYetImplemented;
packages/react-reconciler/src/ReactFiberUnwindWork.js
+2
-2
@@ -12,7 +12,7 @@ import type {FiberRoot} from './ReactFiberRoot';
12
import type {ExpirationTime} from './ReactFiberExpirationTime';
13
import type {CapturedValue} from './ReactCapturedValue';
14
import type {Update} from './ReactUpdateQueue';
15
-import type {Thenable} from './ReactFiberScheduler.old';
15
+import type {Thenable} from './ReactFiberScheduler';
16
import type {SuspenseState} from './ReactFiberSuspenseComponent';
17
18
import {unstable_wrap as Schedule_tracing_wrap} from 'scheduler/tracing';
@@ -67,7 +67,7 @@ import {
67
isAlreadyFailedLegacyErrorBoundary,
68
pingSuspendedRoot,
69
resolveRetryThenable,
70
-} from './ReactFiberScheduler.old';
70
+} from './ReactFiberScheduler';
71
72
import invariant from 'shared/invariant';
73
import maxSigned31BitInt from './maxSigned31BitInt';
packages/shared/ReactFeatureFlags.js
+4
@@ -64,3 +64,7 @@ export const warnAboutDeprecatedSetNativeProps = false;
64
65
// Experimental React Events support. Only used in www builds for now.
66
export const enableEventAPI = false;
67
+
68
+// Enables rewritten version of ReactFiberScheduler. Added in case we need to
69
+// quickly revert it.
70
+export const enableNewScheduler = false;
packages/shared/forks/ReactFeatureFlags.native-fb.js
+1
@@ -31,6 +31,7 @@ export const replayFailedUnitOfWorkWithInvokeGuardedCallback = __DEV__;
31
export const warnAboutDeprecatedLifecycles = true;
32
export const warnAboutDeprecatedSetNativeProps = true;
33
export const enableEventAPI = false;
34
+export const enableNewScheduler = false;
35
36
// Only used in www builds.
37
export function addUserTimingListener() {
packages/shared/forks/ReactFeatureFlags.native-oss.js
+1
@@ -28,6 +28,7 @@ export const warnAboutShorthandPropertyCollision = false;
28
export const enableSchedulerDebugging = false;
29
export const warnAboutDeprecatedSetNativeProps = false;
30
export const enableEventAPI = false;
31
+export const enableNewScheduler = false;
32
33
// Only used in www builds.
34
export function addUserTimingListener() {
packages/shared/forks/ReactFeatureFlags.persistent.js
+1
@@ -28,6 +28,7 @@ export const warnAboutShorthandPropertyCollision = false;
28
export const enableSchedulerDebugging = false;
29
export const warnAboutDeprecatedSetNativeProps = false;
30
export const enableEventAPI = false;
31
+export const enableNewScheduler = false;
32
33
// Only used in www builds.
34
export function addUserTimingListener() {
packages/shared/forks/ReactFeatureFlags.test-renderer.js
+1
@@ -28,6 +28,7 @@ export const warnAboutShorthandPropertyCollision = false;
28
export const enableSchedulerDebugging = false;
29
export const warnAboutDeprecatedSetNativeProps = false;
30
export const enableEventAPI = false;
31
+export const enableNewScheduler = false;
32
33
// Only used in www builds.
34
export function addUserTimingListener() {
packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
+1
@@ -26,6 +26,7 @@ export const warnAboutDeprecatedSetNativeProps = false;
26
export const disableJavaScriptURLs = false;
27
export const disableYielding = false;
28
export const enableEventAPI = true;
29
+export const enableNewScheduler = false;
30
31
// Only used in www builds.
32
export function addUserTimingListener() {
packages/shared/forks/ReactFeatureFlags.www.js
+5
@@ -39,6 +39,11 @@ export const enableStableConcurrentModeAPIs = false;
39
40
export const enableSuspenseServerRenderer = true;
41
42
+// I've chosen to make this a static flag instead of a dynamic flag controlled
43
+// by a GK so that it doesn't increase bundle size. It should still be easy
44
+// to rollback by reverting the commit that turns this on.
45
+export const enableNewScheduler = false;
46
+
47
let refCount = 0;
48
export function addUserTimingListener() {
49
if (__DEV__) {
scripts/jest/config.source-new-scheduler.js
new
+11
@@ -0,0 +1,11 @@
1
+'use strict';
2
+
3
+const baseConfig = require('./config.base');
4
+
5
+module.exports = Object.assign({}, baseConfig, {
6
+ setupFiles: [
7
+ ...baseConfig.setupFiles,
8
+ require.resolve('./setupNewScheduler.js'),
9
+ require.resolve('./setupHostConfigs.js'),
10
+ ],
11
+});
scripts/jest/setupNewScheduler.js
new
+7
@@ -0,0 +1,7 @@
1
+'use strict';
2
+
3
+jest.mock('shared/ReactFeatureFlags', () => {
4
+ const ReactFeatureFlags = require.requireActual('shared/ReactFeatureFlags');
5
+ ReactFeatureFlags.enableNewScheduler = true;
6
+ return ReactFeatureFlags;
7
+});