Flow: typing of Scheduler (#25317)
Enables well formed exports for /scheduler. Some of the modules there were missing `@flow` and were therefore completely unchecked (despite some spurious types sprinkled around).
Jan Kassens committed
Sep 23, 2022 at 10:28 UTC
135e33c954ebc084e70fa765c07007ebddf18361
9 files changed
+120
-46
packages/scheduler/src/SchedulerFeatureFlags.js
+1
@@ -4,6 +4,7 @@
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 strict
8
*/
9
10
export const enableSchedulerDebugging = false;
packages/scheduler/src/SchedulerMinHeap.js
+8
-7
@@ -7,23 +7,24 @@
7
* @flow strict
8
*/
9
10
-type Heap = Array<Node>;
10
+type Heap<T: Node> = Array<T>;
11
type Node = {
12
id: number,
13
sortIndex: number,
14
+ ...
15
};
16
16
-export function push(heap: Heap, node: Node): void {
17
+export function push<T: Node>(heap: Heap<T>, node: T): void {
18
const index = heap.length;
19
heap.push(node);
20
siftUp(heap, node, index);
21
}
22
22
-export function peek(heap: Heap): Node | null {
23
+export function peek<T: Node>(heap: Heap<T>): T | null {
24
return heap.length === 0 ? null : heap[0];
25
}
26
26
-export function pop(heap: Heap): Node | null {
27
+export function pop<T: Node>(heap: Heap<T>): T | null {
28
if (heap.length === 0) {
29
return null;
30
}
@@ -36,7 +37,7 @@ export function pop(heap: Heap): Node | null {
37
return first;
38
}
39
39
-function siftUp(heap, node, i) {
40
+function siftUp<T: Node>(heap: Heap<T>, node: T, i: number): void {
41
let index = i;
42
while (index > 0) {
43
const parentIndex = (index - 1) >>> 1;
@@ -53,7 +54,7 @@ function siftUp(heap, node, i) {
54
}
55
}
56
56
-function siftDown(heap, node, i) {
57
+function siftDown<T: Node>(heap: Heap<T>, node: T, i: number): void {
58
let index = i;
59
const length = heap.length;
60
const halfLength = length >>> 1;
@@ -85,7 +86,7 @@ function siftDown(heap, node, i) {
86
}
87
}
88
88
-function compare(a, b) {
89
+function compare(a: Node, b: Node) {
90
// Compare sort index first, then task id.
91
const diff = a.sortIndex - b.sortIndex;
92
return diff !== 0 ? diff : a.id - b.id;
packages/scheduler/src/SchedulerPriorities.js
+1
-1
@@ -4,7 +4,7 @@
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
7
+ * @flow strict
8
*/
9
10
export type PriorityLevel = 0 | 1 | 2 | 3 | 4 | 5;
packages/scheduler/src/forks/Scheduler.js
+53
-15
@@ -4,10 +4,13 @@
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
/* eslint-disable no-var */
11
12
+import type {PriorityLevel} from '../SchedulerPriorities';
13
+
14
import {
15
enableSchedulerDebugging,
16
enableProfiling,
@@ -41,7 +44,19 @@ import {
44
startLoggingProfilingEvents,
45
} from '../SchedulerProfiling';
46
44
-let getCurrentTime;
47
+export type Callback = boolean => ?Callback;
48
+
49
+type Task = {
50
+ id: number,
51
+ callback: Callback | null,
52
+ priorityLevel: PriorityLevel,
53
+ startTime: number,
54
+ expirationTime: number,
55
+ sortIndex: number,
56
+ isQueued?: boolean,
57
+};
58
+
59
+let getCurrentTime: () => number | DOMHighResTimeStamp;
60
const hasPerformanceNow =
61
typeof performance === 'object' && typeof performance.now === 'function';
62
@@ -96,7 +111,9 @@ const localSetImmediate =
111
112
const isInputPending =
113
typeof navigator !== 'undefined' &&
114
+ // $FlowFixMe[prop-missing]
115
navigator.scheduling !== undefined &&
116
+ // $FlowFixMe[incompatible-type]
117
navigator.scheduling.isInputPending !== undefined
118
? navigator.scheduling.isInputPending.bind(navigator.scheduling)
119
: null;
@@ -247,7 +264,10 @@ function workLoop(hasTimeRemaining, initialTime) {
264
}
265
}
266
250
-function unstable_runWithPriority(priorityLevel, eventHandler) {
267
+function unstable_runWithPriority<T>(
268
+ priorityLevel: PriorityLevel,
269
+ eventHandler: () => T,
270
+): T {
271
switch (priorityLevel) {
272
case ImmediatePriority:
273
case UserBlockingPriority:
@@ -269,7 +289,7 @@ function unstable_runWithPriority(priorityLevel, eventHandler) {
289
}
290
}
291
272
-function unstable_next(eventHandler) {
292
+function unstable_next<T>(eventHandler: () => T): T {
293
var priorityLevel;
294
switch (currentPriorityLevel) {
295
case ImmediatePriority:
@@ -294,8 +314,9 @@ function unstable_next(eventHandler) {
314
}
315
}
316
297
-function unstable_wrapCallback(callback) {
317
+function unstable_wrapCallback<T: (...Array<mixed>) => mixed>(callback: T): T {
318
var parentPriorityLevel = currentPriorityLevel;
319
+ // $FlowFixMe[incompatible-return]
320
return function() {
321
// This is a fork of runWithPriority, inlined for performance.
322
var previousPriorityLevel = currentPriorityLevel;
@@ -309,7 +330,11 @@ function unstable_wrapCallback(callback) {
330
};
331
}
332
312
-function unstable_scheduleCallback(priorityLevel, callback, options) {
333
+function unstable_scheduleCallback(
334
+ priorityLevel: PriorityLevel,
335
+ callback: Callback,
336
+ options?: {delay: number},
337
+): Task {
338
var currentTime = getCurrentTime();
339
340
var startTime;
@@ -346,7 +371,7 @@ function unstable_scheduleCallback(priorityLevel, callback, options) {
371
372
var expirationTime = startTime + timeout;
373
349
- var newTask = {
374
+ var newTask: Task = {
375
id: taskIdCounter++,
376
callback,
377
priorityLevel,
@@ -403,11 +428,11 @@ function unstable_continueExecution() {
428
}
429
}
430
406
-function unstable_getFirstCallbackNode() {
431
+function unstable_getFirstCallbackNode(): Task | null {
432
return peek(taskQueue);
433
}
434
410
-function unstable_cancelCallback(task) {
435
+function unstable_cancelCallback(task: Task) {
436
if (enableProfiling) {
437
if (task.isQueued) {
438
const currentTime = getCurrentTime();
@@ -422,13 +447,18 @@ function unstable_cancelCallback(task) {
447
task.callback = null;
448
}
449
425
-function unstable_getCurrentPriorityLevel() {
450
+function unstable_getCurrentPriorityLevel(): PriorityLevel {
451
return currentPriorityLevel;
452
}
453
454
let isMessageLoopRunning = false;
430
-let scheduledHostCallback = null;
431
-let taskTimeoutID = -1;
455
+let scheduledHostCallback:
456
+ | null
457
+ | ((
458
+ hasTimeRemaining: boolean,
459
+ initialTime: DOMHighResTimeStamp | number,
460
+ ) => boolean) = null;
461
+let taskTimeoutID: TimeoutID = (-1: any);
462
463
// Scheduler periodically yields in case there is other work on the main
464
// thread, like user events. By default, it yields multiple times per frame.
@@ -441,7 +471,7 @@ let startTime = -1;
471
472
let needsPaint = false;
473
444
-function shouldYieldToHost() {
474
+function shouldYieldToHost(): boolean {
475
const timeElapsed = getCurrentTime() - startTime;
476
if (timeElapsed < frameInterval) {
477
// The main thread has only been blocked for a really short amount of time;
@@ -490,7 +520,9 @@ function requestPaint() {
520
if (
521
enableIsInputPending &&
522
navigator !== undefined &&
523
+ // $FlowFixMe[prop-missing]
524
navigator.scheduling !== undefined &&
525
+ // $FlowFixMe[incompatible-type]
526
navigator.scheduling.isInputPending !== undefined
527
) {
528
needsPaint = true;
@@ -499,7 +531,7 @@ function requestPaint() {
531
// Since we yield every frame regardless, `requestPaint` has no effect.
532
}
533
502
-function forceFrameRate(fps) {
534
+function forceFrameRate(fps: number) {
535
if (fps < 0 || fps > 125) {
536
// Using console['error'] to evade Babel and ESLint
537
console['error'](
@@ -579,6 +611,7 @@ if (typeof localSetImmediate === 'function') {
611
} else {
612
// We should only fallback here in non-browser environments.
613
schedulePerformWorkUntilDeadline = () => {
614
+ // $FlowFixMe[not-a-function] nullable value
615
localSetTimeout(performWorkUntilDeadline, 0);
616
};
617
}
@@ -592,14 +625,16 @@ function requestHostCallback(callback) {
625
}
626
627
function requestHostTimeout(callback, ms) {
628
+ // $FlowFixMe[not-a-function] nullable value
629
taskTimeoutID = localSetTimeout(() => {
630
callback(getCurrentTime());
631
}, ms);
632
}
633
634
function cancelHostTimeout() {
635
+ // $FlowFixMe[not-a-function] nullable value
636
localClearTimeout(taskTimeoutID);
602
- taskTimeoutID = -1;
637
+ taskTimeoutID = ((-1: any): TimeoutID);
638
}
639
640
export {
@@ -623,7 +658,10 @@ export {
658
forceFrameRate as unstable_forceFrameRate,
659
};
660
626
-export const unstable_Profiling = enableProfiling
661
+export const unstable_Profiling: {
662
+ startLoggingProfilingEvents(): void,
663
+ stopLoggingProfilingEvents(): ArrayBuffer | null,
664
+} | null = enableProfiling
665
? {
666
startLoggingProfilingEvents,
667
stopLoggingProfilingEvents,
packages/scheduler/src/forks/SchedulerFeatureFlags.www-dynamic.js
-1
@@ -3,7 +3,6 @@
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
- *
6
*/
7
8
// In www, these flags are controlled by GKs. Because most GKs have some
packages/scheduler/src/forks/SchedulerFeatureFlags.www.js
+4
-1
@@ -4,8 +4,10 @@
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
+// $FlowFixMe[cannot-resolve-module]
11
const dynamicFeatureFlags = require('SchedulerFeatureFlags');
12
13
// Re-export dynamic flags from the www version.
@@ -19,4 +21,5 @@ export const {
21
maxYieldMs,
22
} = dynamicFeatureFlags;
23
22
-export const enableProfiling = __PROFILE__ && enableProfilingFeatureFlag;
24
+export const enableProfiling: boolean =
25
+ __PROFILE__ && enableProfilingFeatureFlag;
packages/scheduler/src/forks/SchedulerMock.js
+49
-18
@@ -4,11 +4,14 @@
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
/* eslint-disable no-var */
11
/* eslint-disable react-internal/prod-error-codes */
12
13
+import type {PriorityLevel} from '../SchedulerPriorities';
14
+
15
import {
16
enableSchedulerDebugging,
17
enableProfiling,
@@ -36,6 +39,18 @@ import {
39
startLoggingProfilingEvents,
40
} from '../SchedulerProfiling';
41
42
+type Callback = boolean => ?Callback;
43
+
44
+type Task = {
45
+ id: number,
46
+ callback: Callback | null,
47
+ priorityLevel: PriorityLevel,
48
+ startTime: number,
49
+ expirationTime: number,
50
+ sortIndex: number,
51
+ isQueued?: boolean,
52
+};
53
+
54
// Max 31 bit integer. The max integer size in V8 for 32-bit systems.
55
// Math.pow(2, 30) - 1
56
// 0b111111111111111111111111111111
@@ -52,7 +67,7 @@ var IDLE_PRIORITY_TIMEOUT = maxSigned31BitInt;
67
68
// Tasks are stored on a min heap
69
var taskQueue = [];
55
-var timerQueue = [];
70
+var timerQueue: Array<Task> = [];
71
72
// Incrementing id counter. Used to maintain insertion order.
73
var taskIdCounter = 1;
@@ -70,7 +85,12 @@ var isHostCallbackScheduled = false;
85
var isHostTimeoutScheduled = false;
86
87
let currentMockTime: number = 0;
73
-let scheduledCallback: ((boolean, number) => void) | null = null;
88
+let scheduledCallback:
89
+ | null
90
+ | ((
91
+ hasTimeRemaining: boolean,
92
+ initialTime: DOMHighResTimeStamp | number,
93
+ ) => boolean) = null;
94
let scheduledTimeout: (number => void) | null = null;
95
let timeoutTime: number = -1;
96
let yieldedValues: Array<mixed> | null = null;
@@ -82,11 +102,11 @@ let shouldYieldForPaint: boolean = false;
102
103
var disableYieldValue = false;
104
85
-function setDisableYieldValue(newValue) {
105
+function setDisableYieldValue(newValue: boolean) {
106
disableYieldValue = newValue;
107
}
108
89
-function advanceTimers(currentTime) {
109
+function advanceTimers(currentTime: number) {
110
// Check for tasks that are no longer delayed and add them to the queue.
111
let timer = peek(timerQueue);
112
while (timer !== null) {
@@ -110,7 +130,7 @@ function advanceTimers(currentTime) {
130
}
131
}
132
113
-function handleTimeout(currentTime) {
133
+function handleTimeout(currentTime: number) {
134
isHostTimeoutScheduled = false;
135
advanceTimers(currentTime);
136
@@ -127,7 +147,7 @@ function handleTimeout(currentTime) {
147
}
148
}
149
130
-function flushWork(hasTimeRemaining, initialTime) {
150
+function flushWork(hasTimeRemaining: boolean, initialTime: number) {
151
if (enableProfiling) {
152
markSchedulerUnsuspended(initialTime);
153
}
@@ -169,7 +189,7 @@ function flushWork(hasTimeRemaining, initialTime) {
189
}
190
}
191
172
-function workLoop(hasTimeRemaining, initialTime) {
192
+function workLoop(hasTimeRemaining, initialTime: number): boolean {
193
let currentTime = initialTime;
194
advanceTimers(currentTime);
195
currentTask = peek(taskQueue);
@@ -238,7 +258,10 @@ function workLoop(hasTimeRemaining, initialTime) {
258
}
259
}
260
241
-function unstable_runWithPriority(priorityLevel, eventHandler) {
261
+function unstable_runWithPriority<T>(
262
+ priorityLevel: PriorityLevel,
263
+ eventHandler: () => T,
264
+): T {
265
switch (priorityLevel) {
266
case ImmediatePriority:
267
case UserBlockingPriority:
@@ -260,7 +283,7 @@ function unstable_runWithPriority(priorityLevel, eventHandler) {
283
}
284
}
285
263
-function unstable_next(eventHandler) {
286
+function unstable_next<T>(eventHandler: () => T): T {
287
var priorityLevel;
288
switch (currentPriorityLevel) {
289
case ImmediatePriority:
@@ -285,8 +308,9 @@ function unstable_next(eventHandler) {
308
}
309
}
310
288
-function unstable_wrapCallback(callback) {
311
+function unstable_wrapCallback<T: (...Array<mixed>) => mixed>(callback: T): T {
312
var parentPriorityLevel = currentPriorityLevel;
313
+ // $FlowFixMe[incompatible-return]
314
return function() {
315
// This is a fork of runWithPriority, inlined for performance.
316
var previousPriorityLevel = currentPriorityLevel;
@@ -300,7 +324,11 @@ function unstable_wrapCallback(callback) {
324
};
325
}
326
303
-function unstable_scheduleCallback(priorityLevel, callback, options) {
327
+function unstable_scheduleCallback(
328
+ priorityLevel: PriorityLevel,
329
+ callback: Callback,
330
+ options?: {delay: number},
331
+): Task {
332
var currentTime = getCurrentTime();
333
334
var startTime;
@@ -337,7 +365,7 @@ function unstable_scheduleCallback(priorityLevel, callback, options) {
365
366
var expirationTime = startTime + timeout;
367
340
- var newTask = {
368
+ var newTask: Task = {
369
id: taskIdCounter++,
370
callback,
371
priorityLevel,
@@ -394,11 +422,11 @@ function unstable_continueExecution() {
422
}
423
}
424
397
-function unstable_getFirstCallbackNode() {
425
+function unstable_getFirstCallbackNode(): Task | null {
426
return peek(taskQueue);
427
}
428
401
-function unstable_cancelCallback(task) {
429
+function unstable_cancelCallback(task: Task) {
430
if (enableProfiling) {
431
if (task.isQueued) {
432
const currentTime = getCurrentTime();
@@ -413,11 +441,11 @@ function unstable_cancelCallback(task) {
441
task.callback = null;
442
}
443
416
-function unstable_getCurrentPriorityLevel() {
444
+function unstable_getCurrentPriorityLevel(): PriorityLevel {
445
return currentPriorityLevel;
446
}
447
420
-function requestHostCallback(callback: boolean => void) {
448
+function requestHostCallback(callback: (boolean, number) => boolean) {
449
scheduledCallback = callback;
450
}
451
@@ -494,7 +522,7 @@ function unstable_flushNumberOfYields(count: number): void {
522
}
523
}
524
497
-function unstable_flushUntilNextPaint(): void {
525
+function unstable_flushUntilNextPaint(): false {
526
if (isFlushing) {
527
throw new Error('Already flushing work.');
528
}
@@ -657,7 +685,10 @@ export {
685
setDisableYieldValue as unstable_setDisableYieldValue,
686
};
687
660
-export const unstable_Profiling = enableProfiling
688
+export const unstable_Profiling: {
689
+ startLoggingProfilingEvents(): void,
690
+ stopLoggingProfilingEvents(): ArrayBuffer | null,
691
+} | null = enableProfiling
692
? {
693
startLoggingProfilingEvents,
694
stopLoggingProfilingEvents,
packages/scheduler/src/forks/SchedulerPostTask.js
+3
-3
@@ -44,7 +44,7 @@ const setTimeout = window.setTimeout;
44
// Use experimental Chrome Scheduler postTask API.
45
const scheduler = global.scheduler;
46
47
-const getCurrentTime = perf.now.bind(perf);
47
+const getCurrentTime: () => DOMHighResTimeStamp = perf.now.bind(perf);
48
49
export const unstable_now = getCurrentTime;
50
@@ -193,7 +193,7 @@ export function unstable_runWithPriority<T>(
193
}
194
}
195
196
-export function unstable_getCurrentPriorityLevel() {
196
+export function unstable_getCurrentPriorityLevel(): PriorityLevel {
197
return currentPriorityLevel_DEPRECATED;
198
}
199
@@ -240,7 +240,7 @@ export function unstable_pauseExecution() {}
240
241
export function unstable_continueExecution() {}
242
243
-export function unstable_getFirstCallbackNode() {
243
+export function unstable_getFirstCallbackNode(): null {
244
return null;
245
}
246
scripts/flow/config/flowconfig
+1
@@ -51,6 +51,7 @@ well_formed_exports=true
51
well_formed_exports.includes=<PROJECT_ROOT>/packages/react-devtools-shared
52
well_formed_exports.includes=<PROJECT_ROOT>/packages/react-devtools-shell
53
well_formed_exports.includes=<PROJECT_ROOT>/packages/react-devtools-timeline
54
+well_formed_exports.includes=<PROJECT_ROOT>/packages/scheduler
55
56
# Substituted by createFlowConfig.js:
57
%REACT_RENDERER_FLOW_OPTIONS%