Refactor SchedulerHostConfigs (#20025)
* Remove SchedulerHostConfigs * Fix builds * Fix forks * Move SchedulerNoDom check to npm/index.js * Fix tests * Add @gate source * Gate build-only test to build test runs
Ricky committed
Nov 2, 2020 at 12:46 UTC
454c2211c09bfa2fd5475c25665f3bbeb6882841
28 files changed
+1365
-548
packages/react-dom/src/__tests__/ReactTestUtilsActUnmockedScheduler-test.js
+3
-1
@@ -33,7 +33,9 @@ function unmount(dom) {
33
34
beforeEach(() => {
35
jest.resetModules();
36
- jest.unmock('scheduler');
36
+ jest.mock('scheduler', () =>
37
+ require.requireActual('scheduler/unstable_no_dom'),
38
+ );
39
yields = [];
40
React = require('react');
41
ReactDOM = require('react-dom');
packages/react-dom/src/__tests__/ReactUnmockedSchedulerWarning-test.internal.js
+1
-1
@@ -17,7 +17,7 @@ function App() {
17
18
beforeEach(() => {
19
jest.resetModules();
20
- jest.unmock('scheduler');
20
+ jest.mock('scheduler', () => require('scheduler/unstable_no_dom'));
21
React = require('react');
22
ReactDOM = require('react-dom');
23
ReactFeatureFlags = require('shared/ReactFeatureFlags');
packages/react-dom/src/__tests__/ReactUnmockedSchedulerWarning-test.js
+3
-1
@@ -16,7 +16,9 @@ function App() {
16
17
beforeEach(() => {
18
jest.resetModules();
19
- jest.unmock('scheduler');
19
+ jest.mock('scheduler', () =>
20
+ require.requireActual('scheduler/unstable_no_dom'),
21
+ );
22
React = require('react');
23
ReactDOM = require('react-dom');
24
});
packages/scheduler/index.js
+1
-1
@@ -7,4 +7,4 @@
7
8
'use strict';
9
10
-export * from './src/Scheduler';
10
+export * from './src/forks/SchedulerDOM';
packages/scheduler/npm/index.js
+3
-1
@@ -1,6 +1,8 @@
1
'use strict';
2
3
-if (process.env.NODE_ENV === 'production') {
3
+if (typeof window === 'undefined' || typeof MessageChannel !== 'function') {
4
+ module.exports = require('./unstable_no_dom');
5
+} else if (process.env.NODE_ENV === 'production') {
6
module.exports = require('./cjs/scheduler.production.min.js');
7
} else {
8
module.exports = require('./cjs/scheduler.development.js');
packages/scheduler/npm/unstable_no_dom.js
new
+7
@@ -0,0 +1,7 @@
1
+'use strict';
2
+
3
+if (process.env.NODE_ENV === 'production') {
4
+ module.exports = require('./cjs/scheduler-unstable_no_dom.production.min.js');
5
+} else {
6
+ module.exports = require('./cjs/scheduler-unstable_no_dom.development.js');
7
+}
packages/scheduler/package.json
+1
@@ -28,6 +28,7 @@
28
"tracing.js",
29
"tracing-profiling.js",
30
"unstable_mock.js",
31
+ "unstable_no_dom.js",
32
"unstable_post_task.js",
33
"cjs/",
34
"umd/"
packages/scheduler/src/__tests__/SchedulerDOM-test.js
renamed
-5
@@ -35,11 +35,6 @@ describe('SchedulerBrowser', () => {
35
36
// Un-mock scheduler
37
jest.mock('scheduler', () => require.requireActual('scheduler'));
38
- jest.mock('scheduler/src/SchedulerHostConfig', () =>
39
- require.requireActual(
40
- 'scheduler/src/forks/SchedulerHostConfig.default.js',
41
- ),
42
- );
38
39
runtime = installMockBrowserRuntime();
40
performance = global.performance;
packages/scheduler/src/__tests__/SchedulerMock-test.js
renamed
packages/scheduler/src/__tests__/SchedulerNoDOM-test.js
+2
-5
@@ -24,11 +24,8 @@ describe('SchedulerNoDOM', () => {
24
jest.useFakeTimers();
25
26
// Un-mock scheduler
27
- jest.mock('scheduler', () => require.requireActual('scheduler'));
28
- jest.mock('scheduler/src/SchedulerHostConfig', () =>
29
- require.requireActual(
30
- 'scheduler/src/forks/SchedulerHostConfig.default.js',
31
- ),
27
+ jest.mock('scheduler', () =>
28
+ require.requireActual('scheduler/unstable_no_dom'),
29
);
30
31
Scheduler = require('scheduler');
packages/scheduler/src/__tests__/SchedulerUMDBundle-test.internal.js
+19
-5
@@ -8,6 +8,13 @@
8
*/
9
'use strict';
10
11
+class MockMessageChannel {
12
+ constructor() {
13
+ this.port1 = jest.fn();
14
+ this.port2 = jest.fn();
15
+ }
16
+}
17
+
18
describe('Scheduling UMD bundle', () => {
19
beforeEach(() => {
20
// Fool SECRET_INTERNALS object into including UMD forwarding methods.
@@ -16,11 +23,18 @@ describe('Scheduling UMD bundle', () => {
23
jest.resetModules();
24
25
jest.mock('scheduler', () => require.requireActual('scheduler'));
19
- jest.mock('scheduler/src/SchedulerHostConfig', () =>
20
- require.requireActual(
21
- 'scheduler/src/forks/SchedulerHostConfig.default.js',
22
- ),
23
- );
26
+
27
+ // Mock a browser environment since we're testing UMD modules.
28
+ global.window = {
29
+ requestAnimationFrame: jest.fn(),
30
+ cancelAnimationFrame: jest.fn(),
31
+ };
32
+ global.MessageChannel = MockMessageChannel;
33
+ });
34
+
35
+ afterEach(() => {
36
+ global.window = undefined;
37
+ global.MessageChannel = undefined;
38
});
39
40
function filterPrivateKeys(name) {
packages/scheduler/src/forks/SchedulerDOM.js
new
+598
@@ -0,0 +1,598 @@
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
+
9
+/* eslint-disable no-var */
10
+
11
+import {
12
+ enableSchedulerDebugging,
13
+ enableProfiling,
14
+} from '../SchedulerFeatureFlags';
15
+
16
+import {push, pop, peek} from '../SchedulerMinHeap';
17
+
18
+// TODO: Use symbols?
19
+import {
20
+ ImmediatePriority,
21
+ UserBlockingPriority,
22
+ NormalPriority,
23
+ LowPriority,
24
+ IdlePriority,
25
+} from '../SchedulerPriorities';
26
+import {
27
+ sharedProfilingBuffer,
28
+ markTaskRun,
29
+ markTaskYield,
30
+ markTaskCompleted,
31
+ markTaskCanceled,
32
+ markTaskErrored,
33
+ markSchedulerSuspended,
34
+ markSchedulerUnsuspended,
35
+ markTaskStart,
36
+ stopLoggingProfilingEvents,
37
+ startLoggingProfilingEvents,
38
+} from '../SchedulerProfiling';
39
+
40
+import {enableIsInputPending} from '../SchedulerFeatureFlags';
41
+
42
+let getCurrentTime;
43
+const hasPerformanceNow =
44
+ typeof performance === 'object' && typeof performance.now === 'function';
45
+
46
+if (hasPerformanceNow) {
47
+ const localPerformance = performance;
48
+ getCurrentTime = () => localPerformance.now();
49
+} else {
50
+ const localDate = Date;
51
+ const initialTime = localDate.now();
52
+ getCurrentTime = () => localDate.now() - initialTime;
53
+}
54
+
55
+// Max 31 bit integer. The max integer size in V8 for 32-bit systems.
56
+// Math.pow(2, 30) - 1
57
+// 0b111111111111111111111111111111
58
+var maxSigned31BitInt = 1073741823;
59
+
60
+// Times out immediately
61
+var IMMEDIATE_PRIORITY_TIMEOUT = -1;
62
+// Eventually times out
63
+var USER_BLOCKING_PRIORITY_TIMEOUT = 250;
64
+var NORMAL_PRIORITY_TIMEOUT = 5000;
65
+var LOW_PRIORITY_TIMEOUT = 10000;
66
+// Never times out
67
+var IDLE_PRIORITY_TIMEOUT = maxSigned31BitInt;
68
+
69
+// Tasks are stored on a min heap
70
+var taskQueue = [];
71
+var timerQueue = [];
72
+
73
+// Incrementing id counter. Used to maintain insertion order.
74
+var taskIdCounter = 1;
75
+
76
+// Pausing the scheduler is useful for debugging.
77
+var isSchedulerPaused = false;
78
+
79
+var currentTask = null;
80
+var currentPriorityLevel = NormalPriority;
81
+
82
+// This is set while performing work, to prevent re-entrancy.
83
+var isPerformingWork = false;
84
+
85
+var isHostCallbackScheduled = false;
86
+var isHostTimeoutScheduled = false;
87
+
88
+// Capture local references to native APIs, in case a polyfill overrides them.
89
+const setTimeout = window.setTimeout;
90
+const clearTimeout = window.clearTimeout;
91
+
92
+if (typeof console !== 'undefined') {
93
+ // TODO: Scheduler no longer requires these methods to be polyfilled. But
94
+ // maybe we want to continue warning if they don't exist, to preserve the
95
+ // option to rely on it in the future?
96
+ const requestAnimationFrame = window.requestAnimationFrame;
97
+ const cancelAnimationFrame = window.cancelAnimationFrame;
98
+
99
+ if (typeof requestAnimationFrame !== 'function') {
100
+ // Using console['error'] to evade Babel and ESLint
101
+ console['error'](
102
+ "This browser doesn't support requestAnimationFrame. " +
103
+ 'Make sure that you load a ' +
104
+ 'polyfill in older browsers. https://reactjs.org/link/react-polyfills',
105
+ );
106
+ }
107
+ if (typeof cancelAnimationFrame !== 'function') {
108
+ // Using console['error'] to evade Babel and ESLint
109
+ console['error'](
110
+ "This browser doesn't support cancelAnimationFrame. " +
111
+ 'Make sure that you load a ' +
112
+ 'polyfill in older browsers. https://reactjs.org/link/react-polyfills',
113
+ );
114
+ }
115
+}
116
+
117
+function advanceTimers(currentTime) {
118
+ // Check for tasks that are no longer delayed and add them to the queue.
119
+ let timer = peek(timerQueue);
120
+ while (timer !== null) {
121
+ if (timer.callback === null) {
122
+ // Timer was cancelled.
123
+ pop(timerQueue);
124
+ } else if (timer.startTime <= currentTime) {
125
+ // Timer fired. Transfer to the task queue.
126
+ pop(timerQueue);
127
+ timer.sortIndex = timer.expirationTime;
128
+ push(taskQueue, timer);
129
+ if (enableProfiling) {
130
+ markTaskStart(timer, currentTime);
131
+ timer.isQueued = true;
132
+ }
133
+ } else {
134
+ // Remaining timers are pending.
135
+ return;
136
+ }
137
+ timer = peek(timerQueue);
138
+ }
139
+}
140
+
141
+function handleTimeout(currentTime) {
142
+ isHostTimeoutScheduled = false;
143
+ advanceTimers(currentTime);
144
+
145
+ if (!isHostCallbackScheduled) {
146
+ if (peek(taskQueue) !== null) {
147
+ isHostCallbackScheduled = true;
148
+ requestHostCallback(flushWork);
149
+ } else {
150
+ const firstTimer = peek(timerQueue);
151
+ if (firstTimer !== null) {
152
+ requestHostTimeout(handleTimeout, firstTimer.startTime - currentTime);
153
+ }
154
+ }
155
+ }
156
+}
157
+
158
+function flushWork(hasTimeRemaining, initialTime) {
159
+ if (enableProfiling) {
160
+ markSchedulerUnsuspended(initialTime);
161
+ }
162
+
163
+ // We'll need a host callback the next time work is scheduled.
164
+ isHostCallbackScheduled = false;
165
+ if (isHostTimeoutScheduled) {
166
+ // We scheduled a timeout but it's no longer needed. Cancel it.
167
+ isHostTimeoutScheduled = false;
168
+ cancelHostTimeout();
169
+ }
170
+
171
+ isPerformingWork = true;
172
+ const previousPriorityLevel = currentPriorityLevel;
173
+ try {
174
+ if (enableProfiling) {
175
+ try {
176
+ return workLoop(hasTimeRemaining, initialTime);
177
+ } catch (error) {
178
+ if (currentTask !== null) {
179
+ const currentTime = getCurrentTime();
180
+ markTaskErrored(currentTask, currentTime);
181
+ currentTask.isQueued = false;
182
+ }
183
+ throw error;
184
+ }
185
+ } else {
186
+ // No catch in prod code path.
187
+ return workLoop(hasTimeRemaining, initialTime);
188
+ }
189
+ } finally {
190
+ currentTask = null;
191
+ currentPriorityLevel = previousPriorityLevel;
192
+ isPerformingWork = false;
193
+ if (enableProfiling) {
194
+ const currentTime = getCurrentTime();
195
+ markSchedulerSuspended(currentTime);
196
+ }
197
+ }
198
+}
199
+
200
+function workLoop(hasTimeRemaining, initialTime) {
201
+ let currentTime = initialTime;
202
+ advanceTimers(currentTime);
203
+ currentTask = peek(taskQueue);
204
+ while (
205
+ currentTask !== null &&
206
+ !(enableSchedulerDebugging && isSchedulerPaused)
207
+ ) {
208
+ if (
209
+ currentTask.expirationTime > currentTime &&
210
+ (!hasTimeRemaining || shouldYieldToHost())
211
+ ) {
212
+ // This currentTask hasn't expired, and we've reached the deadline.
213
+ break;
214
+ }
215
+ const callback = currentTask.callback;
216
+ if (typeof callback === 'function') {
217
+ currentTask.callback = null;
218
+ currentPriorityLevel = currentTask.priorityLevel;
219
+ const didUserCallbackTimeout = currentTask.expirationTime <= currentTime;
220
+ markTaskRun(currentTask, currentTime);
221
+ const continuationCallback = callback(didUserCallbackTimeout);
222
+ currentTime = getCurrentTime();
223
+ if (typeof continuationCallback === 'function') {
224
+ currentTask.callback = continuationCallback;
225
+ markTaskYield(currentTask, currentTime);
226
+ } else {
227
+ if (enableProfiling) {
228
+ markTaskCompleted(currentTask, currentTime);
229
+ currentTask.isQueued = false;
230
+ }
231
+ if (currentTask === peek(taskQueue)) {
232
+ pop(taskQueue);
233
+ }
234
+ }
235
+ advanceTimers(currentTime);
236
+ } else {
237
+ pop(taskQueue);
238
+ }
239
+ currentTask = peek(taskQueue);
240
+ }
241
+ // Return whether there's additional work
242
+ if (currentTask !== null) {
243
+ return true;
244
+ } else {
245
+ const firstTimer = peek(timerQueue);
246
+ if (firstTimer !== null) {
247
+ requestHostTimeout(handleTimeout, firstTimer.startTime - currentTime);
248
+ }
249
+ return false;
250
+ }
251
+}
252
+
253
+function unstable_runWithPriority(priorityLevel, eventHandler) {
254
+ switch (priorityLevel) {
255
+ case ImmediatePriority:
256
+ case UserBlockingPriority:
257
+ case NormalPriority:
258
+ case LowPriority:
259
+ case IdlePriority:
260
+ break;
261
+ default:
262
+ priorityLevel = NormalPriority;
263
+ }
264
+
265
+ var previousPriorityLevel = currentPriorityLevel;
266
+ currentPriorityLevel = priorityLevel;
267
+
268
+ try {
269
+ return eventHandler();
270
+ } finally {
271
+ currentPriorityLevel = previousPriorityLevel;
272
+ }
273
+}
274
+
275
+function unstable_next(eventHandler) {
276
+ var priorityLevel;
277
+ switch (currentPriorityLevel) {
278
+ case ImmediatePriority:
279
+ case UserBlockingPriority:
280
+ case NormalPriority:
281
+ // Shift down to normal priority
282
+ priorityLevel = NormalPriority;
283
+ break;
284
+ default:
285
+ // Anything lower than normal priority should remain at the current level.
286
+ priorityLevel = currentPriorityLevel;
287
+ break;
288
+ }
289
+
290
+ var previousPriorityLevel = currentPriorityLevel;
291
+ currentPriorityLevel = priorityLevel;
292
+
293
+ try {
294
+ return eventHandler();
295
+ } finally {
296
+ currentPriorityLevel = previousPriorityLevel;
297
+ }
298
+}
299
+
300
+function unstable_wrapCallback(callback) {
301
+ var parentPriorityLevel = currentPriorityLevel;
302
+ return function() {
303
+ // This is a fork of runWithPriority, inlined for performance.
304
+ var previousPriorityLevel = currentPriorityLevel;
305
+ currentPriorityLevel = parentPriorityLevel;
306
+
307
+ try {
308
+ return callback.apply(this, arguments);
309
+ } finally {
310
+ currentPriorityLevel = previousPriorityLevel;
311
+ }
312
+ };
313
+}
314
+
315
+function unstable_scheduleCallback(priorityLevel, callback, options) {
316
+ var currentTime = getCurrentTime();
317
+
318
+ var startTime;
319
+ if (typeof options === 'object' && options !== null) {
320
+ var delay = options.delay;
321
+ if (typeof delay === 'number' && delay > 0) {
322
+ startTime = currentTime + delay;
323
+ } else {
324
+ startTime = currentTime;
325
+ }
326
+ } else {
327
+ startTime = currentTime;
328
+ }
329
+
330
+ var timeout;
331
+ switch (priorityLevel) {
332
+ case ImmediatePriority:
333
+ timeout = IMMEDIATE_PRIORITY_TIMEOUT;
334
+ break;
335
+ case UserBlockingPriority:
336
+ timeout = USER_BLOCKING_PRIORITY_TIMEOUT;
337
+ break;
338
+ case IdlePriority:
339
+ timeout = IDLE_PRIORITY_TIMEOUT;
340
+ break;
341
+ case LowPriority:
342
+ timeout = LOW_PRIORITY_TIMEOUT;
343
+ break;
344
+ case NormalPriority:
345
+ default:
346
+ timeout = NORMAL_PRIORITY_TIMEOUT;
347
+ break;
348
+ }
349
+
350
+ var expirationTime = startTime + timeout;
351
+
352
+ var newTask = {
353
+ id: taskIdCounter++,
354
+ callback,
355
+ priorityLevel,
356
+ startTime,
357
+ expirationTime,
358
+ sortIndex: -1,
359
+ };
360
+ if (enableProfiling) {
361
+ newTask.isQueued = false;
362
+ }
363
+
364
+ if (startTime > currentTime) {
365
+ // This is a delayed task.
366
+ newTask.sortIndex = startTime;
367
+ push(timerQueue, newTask);
368
+ if (peek(taskQueue) === null && newTask === peek(timerQueue)) {
369
+ // All tasks are delayed, and this is the task with the earliest delay.
370
+ if (isHostTimeoutScheduled) {
371
+ // Cancel an existing timeout.
372
+ cancelHostTimeout();
373
+ } else {
374
+ isHostTimeoutScheduled = true;
375
+ }
376
+ // Schedule a timeout.
377
+ requestHostTimeout(handleTimeout, startTime - currentTime);
378
+ }
379
+ } else {
380
+ newTask.sortIndex = expirationTime;
381
+ push(taskQueue, newTask);
382
+ if (enableProfiling) {
383
+ markTaskStart(newTask, currentTime);
384
+ newTask.isQueued = true;
385
+ }
386
+ // Schedule a host callback, if needed. If we're already performing work,
387
+ // wait until the next time we yield.
388
+ if (!isHostCallbackScheduled && !isPerformingWork) {
389
+ isHostCallbackScheduled = true;
390
+ requestHostCallback(flushWork);
391
+ }
392
+ }
393
+
394
+ return newTask;
395
+}
396
+
397
+function unstable_pauseExecution() {
398
+ isSchedulerPaused = true;
399
+}
400
+
401
+function unstable_continueExecution() {
402
+ isSchedulerPaused = false;
403
+ if (!isHostCallbackScheduled && !isPerformingWork) {
404
+ isHostCallbackScheduled = true;
405
+ requestHostCallback(flushWork);
406
+ }
407
+}
408
+
409
+function unstable_getFirstCallbackNode() {
410
+ return peek(taskQueue);
411
+}
412
+
413
+function unstable_cancelCallback(task) {
414
+ if (enableProfiling) {
415
+ if (task.isQueued) {
416
+ const currentTime = getCurrentTime();
417
+ markTaskCanceled(task, currentTime);
418
+ task.isQueued = false;
419
+ }
420
+ }
421
+
422
+ // Null out the callback to indicate the task has been canceled. (Can't
423
+ // remove from the queue because you can't remove arbitrary nodes from an
424
+ // array based heap, only the first one.)
425
+ task.callback = null;
426
+}
427
+
428
+function unstable_getCurrentPriorityLevel() {
429
+ return currentPriorityLevel;
430
+}
431
+
432
+let isMessageLoopRunning = false;
433
+let scheduledHostCallback = null;
434
+let taskTimeoutID = -1;
435
+
436
+// Scheduler periodically yields in case there is other work on the main
437
+// thread, like user events. By default, it yields multiple times per frame.
438
+// It does not attempt to align with frame boundaries, since most tasks don't
439
+// need to be frame aligned; for those that do, use requestAnimationFrame.
440
+let yieldInterval = 5;
441
+let deadline = 0;
442
+
443
+// TODO: Make this configurable
444
+// TODO: Adjust this based on priority?
445
+const maxYieldInterval = 300;
446
+let needsPaint = false;
447
+
448
+function shouldYieldToHost() {
449
+ if (
450
+ enableIsInputPending &&
451
+ navigator !== undefined &&
452
+ navigator.scheduling !== undefined &&
453
+ navigator.scheduling.isInputPending !== undefined
454
+ ) {
455
+ const scheduling = navigator.scheduling;
456
+ const currentTime = getCurrentTime();
457
+ if (currentTime >= deadline) {
458
+ // There's no time left. We may want to yield control of the main
459
+ // thread, so the browser can perform high priority tasks. The main ones
460
+ // are painting and user input. If there's a pending paint or a pending
461
+ // input, then we should yield. But if there's neither, then we can
462
+ // yield less often while remaining responsive. We'll eventually yield
463
+ // regardless, since there could be a pending paint that wasn't
464
+ // accompanied by a call to `requestPaint`, or other main thread tasks
465
+ // like network events.
466
+ if (needsPaint || scheduling.isInputPending()) {
467
+ // There is either a pending paint or a pending input.
468
+ return true;
469
+ }
470
+ // There's no pending input. Only yield if we've reached the max
471
+ // yield interval.
472
+ return currentTime >= maxYieldInterval;
473
+ } else {
474
+ // There's still time left in the frame.
475
+ return false;
476
+ }
477
+ } else {
478
+ // `isInputPending` is not available. Since we have no way of knowing if
479
+ // there's pending input, always yield at the end of the frame.
480
+ return getCurrentTime() >= deadline;
481
+ }
482
+}
483
+
484
+function requestPaint() {
485
+ if (
486
+ enableIsInputPending &&
487
+ navigator !== undefined &&
488
+ navigator.scheduling !== undefined &&
489
+ navigator.scheduling.isInputPending !== undefined
490
+ ) {
491
+ needsPaint = true;
492
+ }
493
+
494
+ // Since we yield every frame regardless, `requestPaint` has no effect.
495
+}
496
+
497
+function forceFrameRate(fps) {
498
+ if (fps < 0 || fps > 125) {
499
+ // Using console['error'] to evade Babel and ESLint
500
+ console['error'](
501
+ 'forceFrameRate takes a positive int between 0 and 125, ' +
502
+ 'forcing frame rates higher than 125 fps is not supported',
503
+ );
504
+ return;
505
+ }
506
+ if (fps > 0) {
507
+ yieldInterval = Math.floor(1000 / fps);
508
+ } else {
509
+ // reset the framerate
510
+ yieldInterval = 5;
511
+ }
512
+}
513
+
514
+const performWorkUntilDeadline = () => {
515
+ if (scheduledHostCallback !== null) {
516
+ const currentTime = getCurrentTime();
517
+ // Yield after `yieldInterval` ms, regardless of where we are in the vsync
518
+ // cycle. This means there's always time remaining at the beginning of
519
+ // the message event.
520
+ deadline = currentTime + yieldInterval;
521
+ const hasTimeRemaining = true;
522
+ try {
523
+ const hasMoreWork = scheduledHostCallback(hasTimeRemaining, currentTime);
524
+ if (!hasMoreWork) {
525
+ isMessageLoopRunning = false;
526
+ scheduledHostCallback = null;
527
+ } else {
528
+ // If there's more work, schedule the next message event at the end
529
+ // of the preceding one.
530
+ port.postMessage(null);
531
+ }
532
+ } catch (error) {
533
+ // If a scheduler task throws, exit the current browser task so the
534
+ // error can be observed.
535
+ port.postMessage(null);
536
+ throw error;
537
+ }
538
+ } else {
539
+ isMessageLoopRunning = false;
540
+ }
541
+ // Yielding to the browser will give it a chance to paint, so we can
542
+ // reset this.
543
+ needsPaint = false;
544
+};
545
+
546
+const channel = new MessageChannel();
547
+const port = channel.port2;
548
+channel.port1.onmessage = performWorkUntilDeadline;
549
+
550
+function requestHostCallback(callback) {
551
+ scheduledHostCallback = callback;
552
+ if (!isMessageLoopRunning) {
553
+ isMessageLoopRunning = true;
554
+ port.postMessage(null);
555
+ }
556
+}
557
+
558
+function requestHostTimeout(callback, ms) {
559
+ taskTimeoutID = setTimeout(() => {
560
+ callback(getCurrentTime());
561
+ }, ms);
562
+}
563
+
564
+function cancelHostTimeout() {
565
+ clearTimeout(taskTimeoutID);
566
+ taskTimeoutID = -1;
567
+}
568
+
569
+const unstable_requestPaint = requestPaint;
570
+
571
+export {
572
+ ImmediatePriority as unstable_ImmediatePriority,
573
+ UserBlockingPriority as unstable_UserBlockingPriority,
574
+ NormalPriority as unstable_NormalPriority,
575
+ IdlePriority as unstable_IdlePriority,
576
+ LowPriority as unstable_LowPriority,
577
+ unstable_runWithPriority,
578
+ unstable_next,
579
+ unstable_scheduleCallback,
580
+ unstable_cancelCallback,
581
+ unstable_wrapCallback,
582
+ unstable_getCurrentPriorityLevel,
583
+ shouldYieldToHost as unstable_shouldYield,
584
+ unstable_requestPaint,
585
+ unstable_continueExecution,
586
+ unstable_pauseExecution,
587
+ unstable_getFirstCallbackNode,
588
+ getCurrentTime as unstable_now,
589
+ forceFrameRate as unstable_forceFrameRate,
590
+};
591
+
592
+export const unstable_Profiling = enableProfiling
593
+ ? {
594
+ startLoggingProfilingEvents,
595
+ stopLoggingProfilingEvents,
596
+ sharedProfilingBuffer,
597
+ }
598
+ : null;
packages/scheduler/src/forks/SchedulerFeatureFlags.www.js
+1
-1
@@ -10,6 +10,6 @@ export const {
10
enableIsInputPending,
11
enableSchedulerDebugging,
12
enableProfiling: enableProfilingFeatureFlag,
13
-} = require('SchedulerFeatureFlags');
13
+} = require('packages/scheduler/src/SchedulerFeatureFlags');
14
15
export const enableProfiling = __PROFILE__ && enableProfilingFeatureFlag;
packages/scheduler/src/forks/SchedulerHostConfig.default.js
deleted
-246
@@ -1,246 +0,0 @@
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
-import {enableIsInputPending} from '../SchedulerFeatureFlags';
9
-
10
-export let requestHostCallback;
11
-export let cancelHostCallback;
12
-export let requestHostTimeout;
13
-export let cancelHostTimeout;
14
-export let shouldYieldToHost;
15
-export let requestPaint;
16
-export let getCurrentTime;
17
-export let forceFrameRate;
18
-
19
-const hasPerformanceNow =
20
- typeof performance === 'object' && typeof performance.now === 'function';
21
-
22
-if (hasPerformanceNow) {
23
- const localPerformance = performance;
24
- getCurrentTime = () => localPerformance.now();
25
-} else {
26
- const localDate = Date;
27
- const initialTime = localDate.now();
28
- getCurrentTime = () => localDate.now() - initialTime;
29
-}
30
-
31
-if (
32
- // If Scheduler runs in a non-DOM environment, it falls back to a naive
33
- // implementation using setTimeout.
34
- typeof window === 'undefined' ||
35
- // Check if MessageChannel is supported, too.
36
- typeof MessageChannel !== 'function'
37
-) {
38
- // If this accidentally gets imported in a non-browser environment, e.g. JavaScriptCore,
39
- // fallback to a naive implementation.
40
- let _callback = null;
41
- let _timeoutID = null;
42
- const _flushCallback = function() {
43
- if (_callback !== null) {
44
- try {
45
- const currentTime = getCurrentTime();
46
- const hasRemainingTime = true;
47
- _callback(hasRemainingTime, currentTime);
48
- _callback = null;
49
- } catch (e) {
50
- setTimeout(_flushCallback, 0);
51
- throw e;
52
- }
53
- }
54
- };
55
- requestHostCallback = function(cb) {
56
- if (_callback !== null) {
57
- // Protect against re-entrancy.
58
- setTimeout(requestHostCallback, 0, cb);
59
- } else {
60
- _callback = cb;
61
- setTimeout(_flushCallback, 0);
62
- }
63
- };
64
- cancelHostCallback = function() {
65
- _callback = null;
66
- };
67
- requestHostTimeout = function(cb, ms) {
68
- _timeoutID = setTimeout(cb, ms);
69
- };
70
- cancelHostTimeout = function() {
71
- clearTimeout(_timeoutID);
72
- };
73
- shouldYieldToHost = function() {
74
- return false;
75
- };
76
- requestPaint = forceFrameRate = function() {};
77
-} else {
78
- // Capture local references to native APIs, in case a polyfill overrides them.
79
- const setTimeout = window.setTimeout;
80
- const clearTimeout = window.clearTimeout;
81
-
82
- if (typeof console !== 'undefined') {
83
- // TODO: Scheduler no longer requires these methods to be polyfilled. But
84
- // maybe we want to continue warning if they don't exist, to preserve the
85
- // option to rely on it in the future?
86
- const requestAnimationFrame = window.requestAnimationFrame;
87
- const cancelAnimationFrame = window.cancelAnimationFrame;
88
-
89
- if (typeof requestAnimationFrame !== 'function') {
90
- // Using console['error'] to evade Babel and ESLint
91
- console['error'](
92
- "This browser doesn't support requestAnimationFrame. " +
93
- 'Make sure that you load a ' +
94
- 'polyfill in older browsers. https://reactjs.org/link/react-polyfills',
95
- );
96
- }
97
- if (typeof cancelAnimationFrame !== 'function') {
98
- // Using console['error'] to evade Babel and ESLint
99
- console['error'](
100
- "This browser doesn't support cancelAnimationFrame. " +
101
- 'Make sure that you load a ' +
102
- 'polyfill in older browsers. https://reactjs.org/link/react-polyfills',
103
- );
104
- }
105
- }
106
-
107
- let isMessageLoopRunning = false;
108
- let scheduledHostCallback = null;
109
- let taskTimeoutID = -1;
110
-
111
- // Scheduler periodically yields in case there is other work on the main
112
- // thread, like user events. By default, it yields multiple times per frame.
113
- // It does not attempt to align with frame boundaries, since most tasks don't
114
- // need to be frame aligned; for those that do, use requestAnimationFrame.
115
- let yieldInterval = 5;
116
- let deadline = 0;
117
-
118
- // TODO: Make this configurable
119
- // TODO: Adjust this based on priority?
120
- const maxYieldInterval = 300;
121
- let needsPaint = false;
122
-
123
- if (
124
- enableIsInputPending &&
125
- navigator !== undefined &&
126
- navigator.scheduling !== undefined &&
127
- navigator.scheduling.isInputPending !== undefined
128
- ) {
129
- const scheduling = navigator.scheduling;
130
- shouldYieldToHost = function() {
131
- const currentTime = getCurrentTime();
132
- if (currentTime >= deadline) {
133
- // There's no time left. We may want to yield control of the main
134
- // thread, so the browser can perform high priority tasks. The main ones
135
- // are painting and user input. If there's a pending paint or a pending
136
- // input, then we should yield. But if there's neither, then we can
137
- // yield less often while remaining responsive. We'll eventually yield
138
- // regardless, since there could be a pending paint that wasn't
139
- // accompanied by a call to `requestPaint`, or other main thread tasks
140
- // like network events.
141
- if (needsPaint || scheduling.isInputPending()) {
142
- // There is either a pending paint or a pending input.
143
- return true;
144
- }
145
- // There's no pending input. Only yield if we've reached the max
146
- // yield interval.
147
- return currentTime >= maxYieldInterval;
148
- } else {
149
- // There's still time left in the frame.
150
- return false;
151
- }
152
- };
153
-
154
- requestPaint = function() {
155
- needsPaint = true;
156
- };
157
- } else {
158
- // `isInputPending` is not available. Since we have no way of knowing if
159
- // there's pending input, always yield at the end of the frame.
160
- shouldYieldToHost = function() {
161
- return getCurrentTime() >= deadline;
162
- };
163
-
164
- // Since we yield every frame regardless, `requestPaint` has no effect.
165
- requestPaint = function() {};
166
- }
167
-
168
- forceFrameRate = function(fps) {
169
- if (fps < 0 || fps > 125) {
170
- // Using console['error'] to evade Babel and ESLint
171
- console['error'](
172
- 'forceFrameRate takes a positive int between 0 and 125, ' +
173
- 'forcing frame rates higher than 125 fps is not supported',
174
- );
175
- return;
176
- }
177
- if (fps > 0) {
178
- yieldInterval = Math.floor(1000 / fps);
179
- } else {
180
- // reset the framerate
181
- yieldInterval = 5;
182
- }
183
- };
184
-
185
- const performWorkUntilDeadline = () => {
186
- if (scheduledHostCallback !== null) {
187
- const currentTime = getCurrentTime();
188
- // Yield after `yieldInterval` ms, regardless of where we are in the vsync
189
- // cycle. This means there's always time remaining at the beginning of
190
- // the message event.
191
- deadline = currentTime + yieldInterval;
192
- const hasTimeRemaining = true;
193
- try {
194
- const hasMoreWork = scheduledHostCallback(
195
- hasTimeRemaining,
196
- currentTime,
197
- );
198
- if (!hasMoreWork) {
199
- isMessageLoopRunning = false;
200
- scheduledHostCallback = null;
201
- } else {
202
- // If there's more work, schedule the next message event at the end
203
- // of the preceding one.
204
- port.postMessage(null);
205
- }
206
- } catch (error) {
207
- // If a scheduler task throws, exit the current browser task so the
208
- // error can be observed.
209
- port.postMessage(null);
210
- throw error;
211
- }
212
- } else {
213
- isMessageLoopRunning = false;
214
- }
215
- // Yielding to the browser will give it a chance to paint, so we can
216
- // reset this.
217
- needsPaint = false;
218
- };
219
-
220
- const channel = new MessageChannel();
221
- const port = channel.port2;
222
- channel.port1.onmessage = performWorkUntilDeadline;
223
-
224
- requestHostCallback = function(callback) {
225
- scheduledHostCallback = callback;
226
- if (!isMessageLoopRunning) {
227
- isMessageLoopRunning = true;
228
- port.postMessage(null);
229
- }
230
- };
231
-
232
- cancelHostCallback = function() {
233
- scheduledHostCallback = null;
234
- };
235
-
236
- requestHostTimeout = function(callback, ms) {
237
- taskTimeoutID = setTimeout(() => {
238
- callback(getCurrentTime());
239
- }, ms);
240
- };
241
-
242
- cancelHostTimeout = function() {
243
- clearTimeout(taskTimeoutID);
244
- taskTimeoutID = -1;
245
- };
246
-}
packages/scheduler/src/forks/SchedulerHostConfig.mock.js
deleted
-225
@@ -1,225 +0,0 @@
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
-let currentTime: number = 0;
11
-let scheduledCallback: ((boolean, number) => void) | null = null;
12
-let scheduledTimeout: (number => void) | null = null;
13
-let timeoutTime: number = -1;
14
-let yieldedValues: Array<mixed> | null = null;
15
-let expectedNumberOfYields: number = -1;
16
-let didStop: boolean = false;
17
-let isFlushing: boolean = false;
18
-let needsPaint: boolean = false;
19
-let shouldYieldForPaint: boolean = false;
20
-
21
-export function requestHostCallback(callback: boolean => void) {
22
- scheduledCallback = callback;
23
-}
24
-
25
-export function cancelHostCallback(): void {
26
- scheduledCallback = null;
27
-}
28
-
29
-export function requestHostTimeout(callback: number => void, ms: number) {
30
- scheduledTimeout = callback;
31
- timeoutTime = currentTime + ms;
32
-}
33
-
34
-export function cancelHostTimeout(): void {
35
- scheduledTimeout = null;
36
- timeoutTime = -1;
37
-}
38
-
39
-export function shouldYieldToHost(): boolean {
40
- if (
41
- (expectedNumberOfYields !== -1 &&
42
- yieldedValues !== null &&
43
- yieldedValues.length >= expectedNumberOfYields) ||
44
- (shouldYieldForPaint && needsPaint)
45
- ) {
46
- // We yielded at least as many values as expected. Stop flushing.
47
- didStop = true;
48
- return true;
49
- }
50
- return false;
51
-}
52
-
53
-export function getCurrentTime(): number {
54
- return currentTime;
55
-}
56
-
57
-export function forceFrameRate() {
58
- // No-op
59
-}
60
-
61
-export function reset() {
62
- if (isFlushing) {
63
- throw new Error('Cannot reset while already flushing work.');
64
- }
65
- currentTime = 0;
66
- scheduledCallback = null;
67
- scheduledTimeout = null;
68
- timeoutTime = -1;
69
- yieldedValues = null;
70
- expectedNumberOfYields = -1;
71
- didStop = false;
72
- isFlushing = false;
73
- needsPaint = false;
74
-}
75
-
76
-// Should only be used via an assertion helper that inspects the yielded values.
77
-export function unstable_flushNumberOfYields(count: number): void {
78
- if (isFlushing) {
79
- throw new Error('Already flushing work.');
80
- }
81
- if (scheduledCallback !== null) {
82
- const cb = scheduledCallback;
83
- expectedNumberOfYields = count;
84
- isFlushing = true;
85
- try {
86
- let hasMoreWork = true;
87
- do {
88
- hasMoreWork = cb(true, currentTime);
89
- } while (hasMoreWork && !didStop);
90
- if (!hasMoreWork) {
91
- scheduledCallback = null;
92
- }
93
- } finally {
94
- expectedNumberOfYields = -1;
95
- didStop = false;
96
- isFlushing = false;
97
- }
98
- }
99
-}
100
-
101
-export function unstable_flushUntilNextPaint(): void {
102
- if (isFlushing) {
103
- throw new Error('Already flushing work.');
104
- }
105
- if (scheduledCallback !== null) {
106
- const cb = scheduledCallback;
107
- shouldYieldForPaint = true;
108
- needsPaint = false;
109
- isFlushing = true;
110
- try {
111
- let hasMoreWork = true;
112
- do {
113
- hasMoreWork = cb(true, currentTime);
114
- } while (hasMoreWork && !didStop);
115
- if (!hasMoreWork) {
116
- scheduledCallback = null;
117
- }
118
- } finally {
119
- shouldYieldForPaint = false;
120
- didStop = false;
121
- isFlushing = false;
122
- }
123
- }
124
-}
125
-
126
-export function unstable_flushExpired() {
127
- if (isFlushing) {
128
- throw new Error('Already flushing work.');
129
- }
130
- if (scheduledCallback !== null) {
131
- isFlushing = true;
132
- try {
133
- const hasMoreWork = scheduledCallback(false, currentTime);
134
- if (!hasMoreWork) {
135
- scheduledCallback = null;
136
- }
137
- } finally {
138
- isFlushing = false;
139
- }
140
- }
141
-}
142
-
143
-export function unstable_flushAllWithoutAsserting(): boolean {
144
- // Returns false if no work was flushed.
145
- if (isFlushing) {
146
- throw new Error('Already flushing work.');
147
- }
148
- if (scheduledCallback !== null) {
149
- const cb = scheduledCallback;
150
- isFlushing = true;
151
- try {
152
- let hasMoreWork = true;
153
- do {
154
- hasMoreWork = cb(true, currentTime);
155
- } while (hasMoreWork);
156
- if (!hasMoreWork) {
157
- scheduledCallback = null;
158
- }
159
- return true;
160
- } finally {
161
- isFlushing = false;
162
- }
163
- } else {
164
- return false;
165
- }
166
-}
167
-
168
-export function unstable_clearYields(): Array<mixed> {
169
- if (yieldedValues === null) {
170
- return [];
171
- }
172
- const values = yieldedValues;
173
- yieldedValues = null;
174
- return values;
175
-}
176
-
177
-export function unstable_flushAll(): void {
178
- if (yieldedValues !== null) {
179
- throw new Error(
180
- 'Log is not empty. Assert on the log of yielded values before ' +
181
- 'flushing additional work.',
182
- );
183
- }
184
- unstable_flushAllWithoutAsserting();
185
- if (yieldedValues !== null) {
186
- throw new Error(
187
- 'While flushing work, something yielded a value. Use an ' +
188
- 'assertion helper to assert on the log of yielded values, e.g. ' +
189
- 'expect(Scheduler).toFlushAndYield([...])',
190
- );
191
- }
192
-}
193
-
194
-export function unstable_yieldValue(value: mixed): void {
195
- // eslint-disable-next-line react-internal/no-production-logging
196
- if (console.log.name === 'disabledLog') {
197
- // If console.log has been patched, we assume we're in render
198
- // replaying and we ignore any values yielding in the second pass.
199
- return;
200
- }
201
- if (yieldedValues === null) {
202
- yieldedValues = [value];
203
- } else {
204
- yieldedValues.push(value);
205
- }
206
-}
207
-
208
-export function unstable_advanceTime(ms: number) {
209
- // eslint-disable-next-line react-internal/no-production-logging
210
- if (console.log.name === 'disabledLog') {
211
- // If console.log has been patched, we assume we're in render
212
- // replaying and we ignore any time advancing in the second pass.
213
- return;
214
- }
215
- currentTime += ms;
216
- if (scheduledTimeout !== null && timeoutTime <= currentTime) {
217
- scheduledTimeout(currentTime);
218
- timeoutTime = -1;
219
- scheduledTimeout = null;
220
- }
221
-}
222
-
223
-export function requestPaint() {
224
- needsPaint = true;
225
-}
packages/scheduler/src/forks/SchedulerMock.js
new
+636
@@ -0,0 +1,636 @@
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
+
9
+/* eslint-disable no-var */
10
+
11
+import {
12
+ enableSchedulerDebugging,
13
+ enableProfiling,
14
+} from '../SchedulerFeatureFlags';
15
+import {push, pop, peek} from '../SchedulerMinHeap';
16
+
17
+// TODO: Use symbols?
18
+import {
19
+ ImmediatePriority,
20
+ UserBlockingPriority,
21
+ NormalPriority,
22
+ LowPriority,
23
+ IdlePriority,
24
+} from '../SchedulerPriorities';
25
+import {
26
+ sharedProfilingBuffer,
27
+ markTaskRun,
28
+ markTaskYield,
29
+ markTaskCompleted,
30
+ markTaskCanceled,
31
+ markTaskErrored,
32
+ markSchedulerSuspended,
33
+ markSchedulerUnsuspended,
34
+ markTaskStart,
35
+ stopLoggingProfilingEvents,
36
+ startLoggingProfilingEvents,
37
+} from '../SchedulerProfiling';
38
+
39
+// Max 31 bit integer. The max integer size in V8 for 32-bit systems.
40
+// Math.pow(2, 30) - 1
41
+// 0b111111111111111111111111111111
42
+var maxSigned31BitInt = 1073741823;
43
+
44
+// Times out immediately
45
+var IMMEDIATE_PRIORITY_TIMEOUT = -1;
46
+// Eventually times out
47
+var USER_BLOCKING_PRIORITY_TIMEOUT = 250;
48
+var NORMAL_PRIORITY_TIMEOUT = 5000;
49
+var LOW_PRIORITY_TIMEOUT = 10000;
50
+// Never times out
51
+var IDLE_PRIORITY_TIMEOUT = maxSigned31BitInt;
52
+
53
+// Tasks are stored on a min heap
54
+var taskQueue = [];
55
+var timerQueue = [];
56
+
57
+// Incrementing id counter. Used to maintain insertion order.
58
+var taskIdCounter = 1;
59
+
60
+// Pausing the scheduler is useful for debugging.
61
+var isSchedulerPaused = false;
62
+
63
+var currentTask = null;
64
+var currentPriorityLevel = NormalPriority;
65
+
66
+// This is set while performing work, to prevent re-entrancy.
67
+var isPerformingWork = false;
68
+
69
+var isHostCallbackScheduled = false;
70
+var isHostTimeoutScheduled = false;
71
+
72
+let currentMockTime: number = 0;
73
+let scheduledCallback: ((boolean, number) => void) | null = null;
74
+let scheduledTimeout: (number => void) | null = null;
75
+let timeoutTime: number = -1;
76
+let yieldedValues: Array<mixed> | null = null;
77
+let expectedNumberOfYields: number = -1;
78
+let didStop: boolean = false;
79
+let isFlushing: boolean = false;
80
+let needsPaint: boolean = false;
81
+let shouldYieldForPaint: boolean = false;
82
+
83
+function advanceTimers(currentTime) {
84
+ // Check for tasks that are no longer delayed and add them to the queue.
85
+ let timer = peek(timerQueue);
86
+ while (timer !== null) {
87
+ if (timer.callback === null) {
88
+ // Timer was cancelled.
89
+ pop(timerQueue);
90
+ } else if (timer.startTime <= currentTime) {
91
+ // Timer fired. Transfer to the task queue.
92
+ pop(timerQueue);
93
+ timer.sortIndex = timer.expirationTime;
94
+ push(taskQueue, timer);
95
+ if (enableProfiling) {
96
+ markTaskStart(timer, currentTime);
97
+ timer.isQueued = true;
98
+ }
99
+ } else {
100
+ // Remaining timers are pending.
101
+ return;
102
+ }
103
+ timer = peek(timerQueue);
104
+ }
105
+}
106
+
107
+function handleTimeout(currentTime) {
108
+ isHostTimeoutScheduled = false;
109
+ advanceTimers(currentTime);
110
+
111
+ if (!isHostCallbackScheduled) {
112
+ if (peek(taskQueue) !== null) {
113
+ isHostCallbackScheduled = true;
114
+ requestHostCallback(flushWork);
115
+ } else {
116
+ const firstTimer = peek(timerQueue);
117
+ if (firstTimer !== null) {
118
+ requestHostTimeout(handleTimeout, firstTimer.startTime - currentTime);
119
+ }
120
+ }
121
+ }
122
+}
123
+
124
+function flushWork(hasTimeRemaining, initialTime) {
125
+ if (enableProfiling) {
126
+ markSchedulerUnsuspended(initialTime);
127
+ }
128
+
129
+ // We'll need a host callback the next time work is scheduled.
130
+ isHostCallbackScheduled = false;
131
+ if (isHostTimeoutScheduled) {
132
+ // We scheduled a timeout but it's no longer needed. Cancel it.
133
+ isHostTimeoutScheduled = false;
134
+ cancelHostTimeout();
135
+ }
136
+
137
+ isPerformingWork = true;
138
+ const previousPriorityLevel = currentPriorityLevel;
139
+ try {
140
+ if (enableProfiling) {
141
+ try {
142
+ return workLoop(hasTimeRemaining, initialTime);
143
+ } catch (error) {
144
+ if (currentTask !== null) {
145
+ const currentTime = getCurrentTime();
146
+ markTaskErrored(currentTask, currentTime);
147
+ currentTask.isQueued = false;
148
+ }
149
+ throw error;
150
+ }
151
+ } else {
152
+ // No catch in prod code path.
153
+ return workLoop(hasTimeRemaining, initialTime);
154
+ }
155
+ } finally {
156
+ currentTask = null;
157
+ currentPriorityLevel = previousPriorityLevel;
158
+ isPerformingWork = false;
159
+ if (enableProfiling) {
160
+ const currentTime = getCurrentTime();
161
+ markSchedulerSuspended(currentTime);
162
+ }
163
+ }
164
+}
165
+
166
+function workLoop(hasTimeRemaining, initialTime) {
167
+ let currentTime = initialTime;
168
+ advanceTimers(currentTime);
169
+ currentTask = peek(taskQueue);
170
+ while (
171
+ currentTask !== null &&
172
+ !(enableSchedulerDebugging && isSchedulerPaused)
173
+ ) {
174
+ if (
175
+ currentTask.expirationTime > currentTime &&
176
+ (!hasTimeRemaining || shouldYieldToHost())
177
+ ) {
178
+ // This currentTask hasn't expired, and we've reached the deadline.
179
+ break;
180
+ }
181
+ const callback = currentTask.callback;
182
+ if (typeof callback === 'function') {
183
+ currentTask.callback = null;
184
+ currentPriorityLevel = currentTask.priorityLevel;
185
+ const didUserCallbackTimeout = currentTask.expirationTime <= currentTime;
186
+ markTaskRun(currentTask, currentTime);
187
+ const continuationCallback = callback(didUserCallbackTimeout);
188
+ currentTime = getCurrentTime();
189
+ if (typeof continuationCallback === 'function') {
190
+ currentTask.callback = continuationCallback;
191
+ markTaskYield(currentTask, currentTime);
192
+ } else {
193
+ if (enableProfiling) {
194
+ markTaskCompleted(currentTask, currentTime);
195
+ currentTask.isQueued = false;
196
+ }
197
+ if (currentTask === peek(taskQueue)) {
198
+ pop(taskQueue);
199
+ }
200
+ }
201
+ advanceTimers(currentTime);
202
+ } else {
203
+ pop(taskQueue);
204
+ }
205
+ currentTask = peek(taskQueue);
206
+ }
207
+ // Return whether there's additional work
208
+ if (currentTask !== null) {
209
+ return true;
210
+ } else {
211
+ const firstTimer = peek(timerQueue);
212
+ if (firstTimer !== null) {
213
+ requestHostTimeout(handleTimeout, firstTimer.startTime - currentTime);
214
+ }
215
+ return false;
216
+ }
217
+}
218
+
219
+function unstable_runWithPriority(priorityLevel, eventHandler) {
220
+ switch (priorityLevel) {
221
+ case ImmediatePriority:
222
+ case UserBlockingPriority:
223
+ case NormalPriority:
224
+ case LowPriority:
225
+ case IdlePriority:
226
+ break;
227
+ default:
228
+ priorityLevel = NormalPriority;
229
+ }
230
+
231
+ var previousPriorityLevel = currentPriorityLevel;
232
+ currentPriorityLevel = priorityLevel;
233
+
234
+ try {
235
+ return eventHandler();
236
+ } finally {
237
+ currentPriorityLevel = previousPriorityLevel;
238
+ }
239
+}
240
+
241
+function unstable_next(eventHandler) {
242
+ var priorityLevel;
243
+ switch (currentPriorityLevel) {
244
+ case ImmediatePriority:
245
+ case UserBlockingPriority:
246
+ case NormalPriority:
247
+ // Shift down to normal priority
248
+ priorityLevel = NormalPriority;
249
+ break;
250
+ default:
251
+ // Anything lower than normal priority should remain at the current level.
252
+ priorityLevel = currentPriorityLevel;
253
+ break;
254
+ }
255
+
256
+ var previousPriorityLevel = currentPriorityLevel;
257
+ currentPriorityLevel = priorityLevel;
258
+
259
+ try {
260
+ return eventHandler();
261
+ } finally {
262
+ currentPriorityLevel = previousPriorityLevel;
263
+ }
264
+}
265
+
266
+function unstable_wrapCallback(callback) {
267
+ var parentPriorityLevel = currentPriorityLevel;
268
+ return function() {
269
+ // This is a fork of runWithPriority, inlined for performance.
270
+ var previousPriorityLevel = currentPriorityLevel;
271
+ currentPriorityLevel = parentPriorityLevel;
272
+
273
+ try {
274
+ return callback.apply(this, arguments);
275
+ } finally {
276
+ currentPriorityLevel = previousPriorityLevel;
277
+ }
278
+ };
279
+}
280
+
281
+function unstable_scheduleCallback(priorityLevel, callback, options) {
282
+ var currentTime = getCurrentTime();
283
+
284
+ var startTime;
285
+ if (typeof options === 'object' && options !== null) {
286
+ var delay = options.delay;
287
+ if (typeof delay === 'number' && delay > 0) {
288
+ startTime = currentTime + delay;
289
+ } else {
290
+ startTime = currentTime;
291
+ }
292
+ } else {
293
+ startTime = currentTime;
294
+ }
295
+
296
+ var timeout;
297
+ switch (priorityLevel) {
298
+ case ImmediatePriority:
299
+ timeout = IMMEDIATE_PRIORITY_TIMEOUT;
300
+ break;
301
+ case UserBlockingPriority:
302
+ timeout = USER_BLOCKING_PRIORITY_TIMEOUT;
303
+ break;
304
+ case IdlePriority:
305
+ timeout = IDLE_PRIORITY_TIMEOUT;
306
+ break;
307
+ case LowPriority:
308
+ timeout = LOW_PRIORITY_TIMEOUT;
309
+ break;
310
+ case NormalPriority:
311
+ default:
312
+ timeout = NORMAL_PRIORITY_TIMEOUT;
313
+ break;
314
+ }
315
+
316
+ var expirationTime = startTime + timeout;
317
+
318
+ var newTask = {
319
+ id: taskIdCounter++,
320
+ callback,
321
+ priorityLevel,
322
+ startTime,
323
+ expirationTime,
324
+ sortIndex: -1,
325
+ };
326
+ if (enableProfiling) {
327
+ newTask.isQueued = false;
328
+ }
329
+
330
+ if (startTime > currentTime) {
331
+ // This is a delayed task.
332
+ newTask.sortIndex = startTime;
333
+ push(timerQueue, newTask);
334
+ if (peek(taskQueue) === null && newTask === peek(timerQueue)) {
335
+ // All tasks are delayed, and this is the task with the earliest delay.
336
+ if (isHostTimeoutScheduled) {
337
+ // Cancel an existing timeout.
338
+ cancelHostTimeout();
339
+ } else {
340
+ isHostTimeoutScheduled = true;
341
+ }
342
+ // Schedule a timeout.
343
+ requestHostTimeout(handleTimeout, startTime - currentTime);
344
+ }
345
+ } else {
346
+ newTask.sortIndex = expirationTime;
347
+ push(taskQueue, newTask);
348
+ if (enableProfiling) {
349
+ markTaskStart(newTask, currentTime);
350
+ newTask.isQueued = true;
351
+ }
352
+ // Schedule a host callback, if needed. If we're already performing work,
353
+ // wait until the next time we yield.
354
+ if (!isHostCallbackScheduled && !isPerformingWork) {
355
+ isHostCallbackScheduled = true;
356
+ requestHostCallback(flushWork);
357
+ }
358
+ }
359
+
360
+ return newTask;
361
+}
362
+
363
+function unstable_pauseExecution() {
364
+ isSchedulerPaused = true;
365
+}
366
+
367
+function unstable_continueExecution() {
368
+ isSchedulerPaused = false;
369
+ if (!isHostCallbackScheduled && !isPerformingWork) {
370
+ isHostCallbackScheduled = true;
371
+ requestHostCallback(flushWork);
372
+ }
373
+}
374
+
375
+function unstable_getFirstCallbackNode() {
376
+ return peek(taskQueue);
377
+}
378
+
379
+function unstable_cancelCallback(task) {
380
+ if (enableProfiling) {
381
+ if (task.isQueued) {
382
+ const currentTime = getCurrentTime();
383
+ markTaskCanceled(task, currentTime);
384
+ task.isQueued = false;
385
+ }
386
+ }
387
+
388
+ // Null out the callback to indicate the task has been canceled. (Can't
389
+ // remove from the queue because you can't remove arbitrary nodes from an
390
+ // array based heap, only the first one.)
391
+ task.callback = null;
392
+}
393
+
394
+function unstable_getCurrentPriorityLevel() {
395
+ return currentPriorityLevel;
396
+}
397
+
398
+function requestHostCallback(callback: boolean => void) {
399
+ scheduledCallback = callback;
400
+}
401
+
402
+function requestHostTimeout(callback: number => void, ms: number) {
403
+ scheduledTimeout = callback;
404
+ timeoutTime = currentMockTime + ms;
405
+}
406
+
407
+function cancelHostTimeout(): void {
408
+ scheduledTimeout = null;
409
+ timeoutTime = -1;
410
+}
411
+
412
+function shouldYieldToHost(): boolean {
413
+ if (
414
+ (expectedNumberOfYields !== -1 &&
415
+ yieldedValues !== null &&
416
+ yieldedValues.length >= expectedNumberOfYields) ||
417
+ (shouldYieldForPaint && needsPaint)
418
+ ) {
419
+ // We yielded at least as many values as expected. Stop flushing.
420
+ didStop = true;
421
+ return true;
422
+ }
423
+ return false;
424
+}
425
+
426
+function getCurrentTime(): number {
427
+ return currentMockTime;
428
+}
429
+
430
+function forceFrameRate() {
431
+ // No-op
432
+}
433
+
434
+function reset() {
435
+ if (isFlushing) {
436
+ throw new Error('Cannot reset while already flushing work.');
437
+ }
438
+ currentMockTime = 0;
439
+ scheduledCallback = null;
440
+ scheduledTimeout = null;
441
+ timeoutTime = -1;
442
+ yieldedValues = null;
443
+ expectedNumberOfYields = -1;
444
+ didStop = false;
445
+ isFlushing = false;
446
+ needsPaint = false;
447
+}
448
+
449
+// Should only be used via an assertion helper that inspects the yielded values.
450
+function unstable_flushNumberOfYields(count: number): void {
451
+ if (isFlushing) {
452
+ throw new Error('Already flushing work.');
453
+ }
454
+ if (scheduledCallback !== null) {
455
+ const cb = scheduledCallback;
456
+ expectedNumberOfYields = count;
457
+ isFlushing = true;
458
+ try {
459
+ let hasMoreWork = true;
460
+ do {
461
+ hasMoreWork = cb(true, currentMockTime);
462
+ } while (hasMoreWork && !didStop);
463
+ if (!hasMoreWork) {
464
+ scheduledCallback = null;
465
+ }
466
+ } finally {
467
+ expectedNumberOfYields = -1;
468
+ didStop = false;
469
+ isFlushing = false;
470
+ }
471
+ }
472
+}
473
+
474
+function unstable_flushUntilNextPaint(): void {
475
+ if (isFlushing) {
476
+ throw new Error('Already flushing work.');
477
+ }
478
+ if (scheduledCallback !== null) {
479
+ const cb = scheduledCallback;
480
+ shouldYieldForPaint = true;
481
+ needsPaint = false;
482
+ isFlushing = true;
483
+ try {
484
+ let hasMoreWork = true;
485
+ do {
486
+ hasMoreWork = cb(true, currentMockTime);
487
+ } while (hasMoreWork && !didStop);
488
+ if (!hasMoreWork) {
489
+ scheduledCallback = null;
490
+ }
491
+ } finally {
492
+ shouldYieldForPaint = false;
493
+ didStop = false;
494
+ isFlushing = false;
495
+ }
496
+ }
497
+}
498
+
499
+function unstable_flushExpired() {
500
+ if (isFlushing) {
501
+ throw new Error('Already flushing work.');
502
+ }
503
+ if (scheduledCallback !== null) {
504
+ isFlushing = true;
505
+ try {
506
+ const hasMoreWork = scheduledCallback(false, currentMockTime);
507
+ if (!hasMoreWork) {
508
+ scheduledCallback = null;
509
+ }
510
+ } finally {
511
+ isFlushing = false;
512
+ }
513
+ }
514
+}
515
+
516
+function unstable_flushAllWithoutAsserting(): boolean {
517
+ // Returns false if no work was flushed.
518
+ if (isFlushing) {
519
+ throw new Error('Already flushing work.');
520
+ }
521
+ if (scheduledCallback !== null) {
522
+ const cb = scheduledCallback;
523
+ isFlushing = true;
524
+ try {
525
+ let hasMoreWork = true;
526
+ do {
527
+ hasMoreWork = cb(true, currentMockTime);
528
+ } while (hasMoreWork);
529
+ if (!hasMoreWork) {
530
+ scheduledCallback = null;
531
+ }
532
+ return true;
533
+ } finally {
534
+ isFlushing = false;
535
+ }
536
+ } else {
537
+ return false;
538
+ }
539
+}
540
+
541
+function unstable_clearYields(): Array<mixed> {
542
+ if (yieldedValues === null) {
543
+ return [];
544
+ }
545
+ const values = yieldedValues;
546
+ yieldedValues = null;
547
+ return values;
548
+}
549
+
550
+function unstable_flushAll(): void {
551
+ if (yieldedValues !== null) {
552
+ throw new Error(
553
+ 'Log is not empty. Assert on the log of yielded values before ' +
554
+ 'flushing additional work.',
555
+ );
556
+ }
557
+ unstable_flushAllWithoutAsserting();
558
+ if (yieldedValues !== null) {
559
+ throw new Error(
560
+ 'While flushing work, something yielded a value. Use an ' +
561
+ 'assertion helper to assert on the log of yielded values, e.g. ' +
562
+ 'expect(Scheduler).toFlushAndYield([...])',
563
+ );
564
+ }
565
+}
566
+
567
+function unstable_yieldValue(value: mixed): void {
568
+ // eslint-disable-next-line react-internal/no-production-logging
569
+ if (console.log.name === 'disabledLog') {
570
+ // If console.log has been patched, we assume we're in render
571
+ // replaying and we ignore any values yielding in the second pass.
572
+ return;
573
+ }
574
+ if (yieldedValues === null) {
575
+ yieldedValues = [value];
576
+ } else {
577
+ yieldedValues.push(value);
578
+ }
579
+}
580
+
581
+function unstable_advanceTime(ms: number) {
582
+ // eslint-disable-next-line react-internal/no-production-logging
583
+ if (console.log.name === 'disabledLog') {
584
+ // If console.log has been patched, we assume we're in render
585
+ // replaying and we ignore any time advancing in the second pass.
586
+ return;
587
+ }
588
+ currentMockTime += ms;
589
+ if (scheduledTimeout !== null && timeoutTime <= currentMockTime) {
590
+ scheduledTimeout(currentMockTime);
591
+ timeoutTime = -1;
592
+ scheduledTimeout = null;
593
+ }
594
+}
595
+
596
+function requestPaint() {
597
+ needsPaint = true;
598
+}
599
+
600
+export {
601
+ ImmediatePriority as unstable_ImmediatePriority,
602
+ UserBlockingPriority as unstable_UserBlockingPriority,
603
+ NormalPriority as unstable_NormalPriority,
604
+ IdlePriority as unstable_IdlePriority,
605
+ LowPriority as unstable_LowPriority,
606
+ unstable_runWithPriority,
607
+ unstable_next,
608
+ unstable_scheduleCallback,
609
+ unstable_cancelCallback,
610
+ unstable_wrapCallback,
611
+ unstable_getCurrentPriorityLevel,
612
+ shouldYieldToHost as unstable_shouldYield,
613
+ requestPaint as unstable_requestPaint,
614
+ unstable_continueExecution,
615
+ unstable_pauseExecution,
616
+ unstable_getFirstCallbackNode,
617
+ getCurrentTime as unstable_now,
618
+ forceFrameRate as unstable_forceFrameRate,
619
+ unstable_flushAllWithoutAsserting,
620
+ unstable_flushNumberOfYields,
621
+ unstable_flushExpired,
622
+ unstable_clearYields,
623
+ unstable_flushUntilNextPaint,
624
+ unstable_flushAll,
625
+ unstable_yieldValue,
626
+ unstable_advanceTime,
627
+ reset,
628
+};
629
+
630
+export const unstable_Profiling = enableProfiling
631
+ ? {
632
+ startLoggingProfilingEvents,
633
+ stopLoggingProfilingEvents,
634
+ sharedProfilingBuffer,
635
+ }
636
+ : null;
packages/scheduler/src/forks/SchedulerNoDOM.js
renamed
+61
-15
@@ -11,17 +11,8 @@
11
import {
12
enableSchedulerDebugging,
13
enableProfiling,
14
-} from './SchedulerFeatureFlags';
15
-import {
16
- requestHostCallback,
17
- requestHostTimeout,
18
- cancelHostTimeout,
19
- shouldYieldToHost,
20
- getCurrentTime,
21
- forceFrameRate,
22
- requestPaint,
23
-} from './SchedulerHostConfig';
24
-import {push, pop, peek} from './SchedulerMinHeap';
14
+} from '../SchedulerFeatureFlags';
15
+import {push, pop, peek} from '../SchedulerMinHeap';
16
17
// TODO: Use symbols?
18
import {
@@ -30,7 +21,7 @@ import {
21
NormalPriority,
22
LowPriority,
23
IdlePriority,
33
-} from './SchedulerPriorities';
24
+} from '../SchedulerPriorities';
25
import {
26
sharedProfilingBuffer,
27
markTaskRun,
@@ -43,7 +34,7 @@ import {
34
markTaskStart,
35
stopLoggingProfilingEvents,
36
startLoggingProfilingEvents,
46
-} from './SchedulerProfiling';
37
+} from '../SchedulerProfiling';
38
39
// Max 31 bit integer. The max integer size in V8 for 32-bit systems.
40
// Math.pow(2, 30) - 1
@@ -78,6 +69,19 @@ var isPerformingWork = false;
69
var isHostCallbackScheduled = false;
70
var isHostTimeoutScheduled = false;
71
72
+let getCurrentTime;
73
+const hasPerformanceNow =
74
+ typeof performance === 'object' && typeof performance.now === 'function';
75
+
76
+if (hasPerformanceNow) {
77
+ const localPerformance = performance;
78
+ getCurrentTime = () => localPerformance.now();
79
+} else {
80
+ const localDate = Date;
81
+ const initialTime = localDate.now();
82
+ getCurrentTime = () => localDate.now() - initialTime;
83
+}
84
+
85
function advanceTimers(currentTime) {
86
// Check for tasks that are no longer delayed and add them to the queue.
87
let timer = peek(timerQueue);
@@ -393,7 +397,49 @@ function unstable_getCurrentPriorityLevel() {
397
return currentPriorityLevel;
398
}
399
396
-const unstable_requestPaint = requestPaint;
400
+// If this accidentally gets imported in a non-browser environment, e.g. JavaScriptCore,
401
+// fallback to a naive implementation.
402
+let _callback = null;
403
+let _timeoutID = null;
404
+const _flushCallback = function() {
405
+ if (_callback !== null) {
406
+ try {
407
+ const currentTime = getCurrentTime();
408
+ const hasRemainingTime = true;
409
+ _callback(hasRemainingTime, currentTime);
410
+ _callback = null;
411
+ } catch (e) {
412
+ setTimeout(_flushCallback, 0);
413
+ throw e;
414
+ }
415
+ }
416
+};
417
+
418
+function requestHostCallback(cb) {
419
+ if (_callback !== null) {
420
+ // Protect against re-entrancy.
421
+ setTimeout(requestHostCallback, 0, cb);
422
+ } else {
423
+ _callback = cb;
424
+ setTimeout(_flushCallback, 0);
425
+ }
426
+}
427
+
428
+function requestHostTimeout(cb, ms) {
429
+ _timeoutID = setTimeout(cb, ms);
430
+}
431
+
432
+function cancelHostTimeout() {
433
+ clearTimeout(_timeoutID);
434
+}
435
+
436
+function shouldYieldToHost() {
437
+ return false;
438
+}
439
+
440
+function forceFrameRate() {}
441
+
442
+function requestPaint() {}
443
444
export {
445
ImmediatePriority as unstable_ImmediatePriority,
@@ -408,7 +454,7 @@ export {
454
unstable_wrapCallback,
455
unstable_getCurrentPriorityLevel,
456
shouldYieldToHost as unstable_shouldYield,
411
- unstable_requestPaint,
457
+ requestPaint as unstable_requestPaint,
458
unstable_continueExecution,
459
unstable_pauseExecution,
460
unstable_getFirstCallbackNode,
packages/scheduler/src/forks/SchedulerPostTask.js
renamed
+2
-2
@@ -7,7 +7,7 @@
7
* @flow
8
*/
9
10
-import type {PriorityLevel} from './SchedulerPriorities';
10
+import type {PriorityLevel} from '../SchedulerPriorities';
11
12
declare class TaskController {
13
constructor(priority?: string): TaskController;
@@ -27,7 +27,7 @@ import {
27
NormalPriority,
28
LowPriority,
29
IdlePriority,
30
-} from './SchedulerPriorities';
30
+} from '../SchedulerPriorities';
31
32
export {
33
ImmediatePriority as unstable_ImmediatePriority,
packages/scheduler/unstable_mock.js
+1
-12
@@ -7,15 +7,4 @@
7
8
'use strict';
9
10
-export * from './src/Scheduler';
11
-
12
-export {
13
- unstable_flushAllWithoutAsserting,
14
- unstable_flushNumberOfYields,
15
- unstable_flushExpired,
16
- unstable_clearYields,
17
- unstable_flushUntilNextPaint,
18
- unstable_flushAll,
19
- unstable_yieldValue,
20
- unstable_advanceTime,
21
-} from './src/SchedulerHostConfig.js';
10
+export * from './src/forks/SchedulerMock';
packages/scheduler/unstable_no_dom.js
renamed
+3
-3
@@ -3,8 +3,8 @@
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
6
*/
7
10
-throw new Error('This module must be shimmed by a specific build.');
8
+'use strict';
9
+
10
+export * from './src/forks/SchedulerNoDOM';
packages/scheduler/unstable_post_task.js
+1
-1
@@ -7,4 +7,4 @@
7
8
'use strict';
9
10
-export * from './src/SchedulerPostTask';
10
+export * from './src/forks/SchedulerPostTask';
packages/shared/__tests__/ReactDOMFrameScheduling-test.js
+1
-5
@@ -15,11 +15,6 @@ describe('ReactDOMFrameScheduling', () => {
15
16
// Un-mock scheduler
17
jest.mock('scheduler', () => require.requireActual('scheduler'));
18
- jest.mock('scheduler/src/SchedulerHostConfig', () =>
19
- require.requireActual(
20
- 'scheduler/src/forks/SchedulerHostConfig.default.js',
21
- ),
22
- );
18
});
19
20
it('warns when requestAnimationFrame is not polyfilled in the browser', () => {
@@ -47,6 +42,7 @@ describe('ReactDOMFrameScheduling', () => {
42
43
// We're just testing importing, not using it.
44
// It is important because even isomorphic components may import it.
45
+ // @gate !source
46
it('can import findDOMNode in Node environment', () => {
47
const previousRAF = global.requestAnimationFrame;
48
const previousRIC = global.requestIdleCallback;
scripts/jest/TestFlags.js
+1
@@ -74,6 +74,7 @@ function getTestFlags() {
74
channel: releaseChannel,
75
modern: releaseChannel === 'modern',
76
classic: releaseChannel === 'classic',
77
+ source: !process.env.IS_BUILD,
78
www,
79
80
...featureFlags,
scripts/jest/config.build.js
+2
@@ -4,6 +4,8 @@ const {readdirSync, statSync} = require('fs');
4
const {join} = require('path');
5
const baseConfig = require('./config.base');
6
7
+process.env.IS_BUILD = true;
8
+
9
// Find all folders in packages/* with package.json
10
const packagesRoot = join(__dirname, '..', '..', 'packages');
11
const packages = readdirSync(packagesRoot).filter(dir => {
scripts/jest/setupHostConfigs.js
-3
@@ -96,6 +96,3 @@ jest.mock('shared/ReactSharedInternals', () =>
96
);
97
98
jest.mock('scheduler', () => require.requireActual('scheduler/unstable_mock'));
99
-jest.mock('scheduler/src/SchedulerHostConfig', () =>
100
- require.requireActual('scheduler/src/forks/SchedulerHostConfig.mock.js')
101
-);
scripts/jest/setupTests.build.js
-3
@@ -1,6 +1,3 @@
1
'use strict';
2
3
jest.mock('scheduler', () => require.requireActual('scheduler/unstable_mock'));
4
-jest.mock('scheduler/src/SchedulerHostConfig', () =>
5
- require.requireActual('scheduler/src/forks/SchedulerHostConfig.mock.js')
6
-);
scripts/rollup/bundles.js
+18
@@ -633,6 +633,24 @@ const bundles = [
633
externals: [],
634
},
635
636
+ /******* React Scheduler No DOM (experimental) *******/
637
+ {
638
+ bundleTypes: [
639
+ NODE_DEV,
640
+ NODE_PROD,
641
+ FB_WWW_DEV,
642
+ FB_WWW_PROD,
643
+ FB_WWW_PROFILING,
644
+ RN_FB_DEV,
645
+ RN_FB_PROD,
646
+ RN_FB_PROFILING,
647
+ ],
648
+ moduleType: ISOMORPHIC,
649
+ entry: 'scheduler/unstable_no_dom',
650
+ global: 'SchedulerNoDOM',
651
+ externals: [],
652
+ },
653
+
654
/******* Jest React (experimental) *******/
655
{
656
bundleTypes: [NODE_DEV, NODE_PROD],
scripts/rollup/forks.js
-12
@@ -201,18 +201,6 @@ const forks = Object.freeze({
201
return 'scheduler/src/SchedulerFeatureFlags';
202
},
203
204
- 'scheduler/src/SchedulerHostConfig': (bundleType, entry, dependencies) => {
205
- if (
206
- entry === 'scheduler/unstable_mock' ||
207
- entry === 'react-noop-renderer' ||
208
- entry === 'react-noop-renderer/persistent' ||
209
- entry === 'react-test-renderer'
210
- ) {
211
- return 'scheduler/src/forks/SchedulerHostConfig.mock';
212
- }
213
- return 'scheduler/src/forks/SchedulerHostConfig.default';
214
- },
215
-
204
'shared/consoleWithStackDev': (bundleType, entry) => {
205
switch (bundleType) {
206
case FB_WWW_DEV: