packages/scheduler/src/forks/SchedulerMock.js JAVASCRIPT 699 lines View on github.com → Search inside
1/**2 * Copyright (c) Meta Platforms, Inc. and affiliates.3 *4 * This source code is licensed under the MIT license found in the5 * LICENSE file in the root directory of this source tree.6 *7 * @flow8 */910/* eslint-disable no-var */11/* eslint-disable react-internal/prod-error-codes */1213import type {PriorityLevel} from '../SchedulerPriorities';1415import {enableProfiling} from '../SchedulerFeatureFlags';16import {push, pop, peek} from '../SchedulerMinHeap';1718// TODO: Use symbols?19import {20  ImmediatePriority,21  UserBlockingPriority,22  NormalPriority,23  LowPriority,24  IdlePriority,25} from '../SchedulerPriorities';26import {27  markTaskRun,28  markTaskYield,29  markTaskCompleted,30  markTaskCanceled,31  markTaskErrored,32  markSchedulerSuspended,33  markSchedulerUnsuspended,34  markTaskStart,35  stopLoggingProfilingEvents,36  startLoggingProfilingEvents,37} from '../SchedulerProfiling';3839type Callback = boolean => ?Callback;4041type Task = {42  id: number,43  callback: Callback | null,44  priorityLevel: PriorityLevel,45  startTime: number,46  expirationTime: number,47  sortIndex: number,48  isQueued?: boolean,49};5051// Max 31 bit integer. The max integer size in V8 for 32-bit systems.52// Math.pow(2, 30) - 153// 0b11111111111111111111111111111154var maxSigned31BitInt = 1073741823;5556// Times out immediately57var IMMEDIATE_PRIORITY_TIMEOUT = -1;58// Eventually times out59var USER_BLOCKING_PRIORITY_TIMEOUT = 250;60var NORMAL_PRIORITY_TIMEOUT = 5000;61var LOW_PRIORITY_TIMEOUT = 10000;62// Never times out63var IDLE_PRIORITY_TIMEOUT = maxSigned31BitInt;6465// Tasks are stored on a min heap66var taskQueue: Array<Task> = [];67var timerQueue: Array<Task> = [];6869// Incrementing id counter. Used to maintain insertion order.70var taskIdCounter = 1;7172var currentTask = null;73var currentPriorityLevel: PriorityLevel = NormalPriority;7475// This is set while performing work, to prevent re-entrance.76var isPerformingWork = false;7778var isHostCallbackScheduled = false;79var isHostTimeoutScheduled = false;8081let currentMockTime: number = 0;82let scheduledCallback:83  | null84  | ((85      hasTimeRemaining: boolean,86      initialTime: DOMHighResTimeStamp | number,87    ) => boolean) = null;88let scheduledTimeout: (number => void) | null = null;89let timeoutTime: number = -1;90let yieldedValues: Array<mixed> | null = null;91let expectedNumberOfYields: number = -1;92let didStop: boolean = false;93let isFlushing: boolean = false;94let needsPaint: boolean = false;95let shouldYieldForPaint: boolean = false;9697var disableYieldValue = false;9899function setDisableYieldValue(newValue: boolean) {100  disableYieldValue = newValue;101}102103function advanceTimers(currentTime: number) {104  // Check for tasks that are no longer delayed and add them to the queue.105  let timer = peek(timerQueue);106  while (timer !== null) {107    if (timer.callback === null) {108      // Timer was cancelled.109      pop(timerQueue);110    } else if (timer.startTime <= currentTime) {111      // Timer fired. Transfer to the task queue.112      pop(timerQueue);113      timer.sortIndex = timer.expirationTime;114      push(taskQueue, timer);115      // $FlowFixMe[constant-condition]116      if (enableProfiling) {117        markTaskStart(timer, currentTime);118        timer.isQueued = true;119      }120    } else {121      // Remaining timers are pending.122      return;123    }124    timer = peek(timerQueue);125  }126}127128function handleTimeout(currentTime: number) {129  isHostTimeoutScheduled = false;130  advanceTimers(currentTime);131132  if (!isHostCallbackScheduled) {133    if (peek(taskQueue) !== null) {134      isHostCallbackScheduled = true;135      requestHostCallback(flushWork);136    } else {137      const firstTimer = peek(timerQueue);138      if (firstTimer !== null) {139        requestHostTimeout(handleTimeout, firstTimer.startTime - currentTime);140      }141    }142  }143}144145function flushWork(hasTimeRemaining: boolean, initialTime: number) {146  // $FlowFixMe[constant-condition]147  if (enableProfiling) {148    markSchedulerUnsuspended(initialTime);149  }150151  // We'll need a host callback the next time work is scheduled.152  isHostCallbackScheduled = false;153  if (isHostTimeoutScheduled) {154    // We scheduled a timeout but it's no longer needed. Cancel it.155    isHostTimeoutScheduled = false;156    cancelHostTimeout();157  }158159  isPerformingWork = true;160  const previousPriorityLevel = currentPriorityLevel;161  try {162    // $FlowFixMe[constant-condition]163    if (enableProfiling) {164      try {165        return workLoop(hasTimeRemaining, initialTime);166      } catch (error) {167        if (currentTask !== null) {168          const currentTime = getCurrentTime();169          // $FlowFixMe[incompatible-call] found when upgrading Flow170          // $FlowFixMe[incompatible-type]171          markTaskErrored(currentTask, currentTime);172          // $FlowFixMe[incompatible-use] found when upgrading Flow173          currentTask.isQueued = false;174        }175        throw error;176      }177    } else {178      // No catch in prod code path.179      return workLoop(hasTimeRemaining, initialTime);180    }181  } finally {182    currentTask = null;183    currentPriorityLevel = previousPriorityLevel;184    isPerformingWork = false;185    // $FlowFixMe[constant-condition]186    if (enableProfiling) {187      const currentTime = getCurrentTime();188      markSchedulerSuspended(currentTime);189    }190  }191}192193function workLoop(hasTimeRemaining: boolean, initialTime: number): boolean {194  let currentTime = initialTime;195  advanceTimers(currentTime);196  currentTask = peek(taskQueue);197  while (currentTask !== null) {198    if (199      currentTask.expirationTime > currentTime &&200      (!hasTimeRemaining || shouldYieldToHost())201    ) {202      // This currentTask hasn't expired, and we've reached the deadline.203      break;204    }205    // $FlowFixMe[incompatible-use] found when upgrading Flow206    const callback = currentTask.callback;207    if (typeof callback === 'function') {208      // $FlowFixMe[incompatible-use] found when upgrading Flow209      currentTask.callback = null;210      // $FlowFixMe[incompatible-use] found when upgrading Flow211      currentPriorityLevel = currentTask.priorityLevel;212      // $FlowFixMe[incompatible-use] found when upgrading Flow213      const didUserCallbackTimeout = currentTask.expirationTime <= currentTime;214      // $FlowFixMe[constant-condition]215      if (enableProfiling) {216        // $FlowFixMe[incompatible-call] found when upgrading Flow217        // $FlowFixMe[incompatible-type]218        markTaskRun(currentTask, currentTime);219      }220      const continuationCallback = callback(didUserCallbackTimeout);221      currentTime = getCurrentTime();222      if (typeof continuationCallback === 'function') {223        // If a continuation is returned, immediately yield to the main thread224        // regardless of how much time is left in the current time slice.225        // $FlowFixMe[incompatible-use] found when upgrading Flow226        currentTask.callback = continuationCallback;227        // $FlowFixMe[constant-condition]228        if (enableProfiling) {229          // $FlowFixMe[incompatible-call] found when upgrading Flow230          // $FlowFixMe[incompatible-type]231          markTaskYield(currentTask, currentTime);232        }233        advanceTimers(currentTime);234235        if (shouldYieldForPaint) {236          needsPaint = true;237          return true;238        } else {239          // If `shouldYieldForPaint` is false, we keep flushing synchronously240          // without yielding to the main thread. This is the behavior of the241          // `toFlushAndYield` and `toFlushAndYieldThrough` testing helpers .242        }243      } else {244        // $FlowFixMe[constant-condition]245        if (enableProfiling) {246          // $FlowFixMe[incompatible-call] found when upgrading Flow247          // $FlowFixMe[incompatible-type]248          markTaskCompleted(currentTask, currentTime);249          // $FlowFixMe[incompatible-use] found when upgrading Flow250          currentTask.isQueued = false;251        }252        if (currentTask === peek(taskQueue)) {253          pop(taskQueue);254        }255        advanceTimers(currentTime);256      }257    } else {258      pop(taskQueue);259    }260    currentTask = peek(taskQueue);261  }262  // Return whether there's additional work263  if (currentTask !== null) {264    return true;265  } else {266    const firstTimer = peek(timerQueue);267    if (firstTimer !== null) {268      requestHostTimeout(handleTimeout, firstTimer.startTime - currentTime);269    }270    return false;271  }272}273274function unstable_runWithPriority<T>(275  priorityLevel: PriorityLevel,276  eventHandler: () => T,277): T {278  switch (priorityLevel) {279    case ImmediatePriority:280    case UserBlockingPriority:281    case NormalPriority:282    case LowPriority:283    case IdlePriority:284      break;285    default:286      priorityLevel = NormalPriority;287  }288289  var previousPriorityLevel = currentPriorityLevel;290  currentPriorityLevel = priorityLevel;291292  try {293    return eventHandler();294  } finally {295    currentPriorityLevel = previousPriorityLevel;296  }297}298299function unstable_next<T>(eventHandler: () => T): T {300  var priorityLevel: PriorityLevel;301  switch (currentPriorityLevel) {302    case ImmediatePriority:303    case UserBlockingPriority:304    case NormalPriority:305      // Shift down to normal priority306      priorityLevel = NormalPriority;307      break;308    default:309      // Anything lower than normal priority should remain at the current level.310      priorityLevel = currentPriorityLevel;311      break;312  }313314  var previousPriorityLevel = currentPriorityLevel;315  currentPriorityLevel = priorityLevel;316317  try {318    return eventHandler();319  } finally {320    currentPriorityLevel = previousPriorityLevel;321  }322}323324function unstable_wrapCallback<T: (...Array<mixed>) => mixed>(callback: T): T {325  var parentPriorityLevel = currentPriorityLevel;326  // $FlowFixMe[incompatible-return]327  // $FlowFixMe[missing-this-annot]328  // $FlowFixMe[incompatible-type]329  return function () {330    // This is a fork of runWithPriority, inlined for performance.331    var previousPriorityLevel = currentPriorityLevel;332    currentPriorityLevel = parentPriorityLevel;333334    try {335      return callback.apply(this, arguments);336    } finally {337      currentPriorityLevel = previousPriorityLevel;338    }339  };340}341342function unstable_scheduleCallback(343  priorityLevel: PriorityLevel,344  callback: Callback,345  options?: {delay: number},346): Task {347  var currentTime = getCurrentTime();348349  var startTime;350  // $FlowFixMe[invalid-compare]351  if (typeof options === 'object' && options !== null) {352    var delay = options.delay;353    if (typeof delay === 'number' && delay > 0) {354      startTime = currentTime + delay;355    } else {356      startTime = currentTime;357    }358  } else {359    startTime = currentTime;360  }361362  var timeout;363  switch (priorityLevel) {364    case ImmediatePriority:365      timeout = IMMEDIATE_PRIORITY_TIMEOUT;366      break;367    case UserBlockingPriority:368      timeout = USER_BLOCKING_PRIORITY_TIMEOUT;369      break;370    case IdlePriority:371      timeout = IDLE_PRIORITY_TIMEOUT;372      break;373    case LowPriority:374      timeout = LOW_PRIORITY_TIMEOUT;375      break;376    case NormalPriority:377    default:378      timeout = NORMAL_PRIORITY_TIMEOUT;379      break;380  }381382  var expirationTime = startTime + timeout;383384  var newTask: Task = {385    id: taskIdCounter++,386    callback,387    priorityLevel,388    startTime,389    expirationTime,390    sortIndex: -1,391  };392  // $FlowFixMe[constant-condition]393  if (enableProfiling) {394    newTask.isQueued = false;395  }396397  if (startTime > currentTime) {398    // This is a delayed task.399    newTask.sortIndex = startTime;400    push(timerQueue, newTask);401    if (peek(taskQueue) === null && newTask === peek(timerQueue)) {402      // All tasks are delayed, and this is the task with the earliest delay.403      if (isHostTimeoutScheduled) {404        // Cancel an existing timeout.405        cancelHostTimeout();406      } else {407        isHostTimeoutScheduled = true;408      }409      // Schedule a timeout.410      requestHostTimeout(handleTimeout, startTime - currentTime);411    }412  } else {413    newTask.sortIndex = expirationTime;414    push(taskQueue, newTask);415    // $FlowFixMe[constant-condition]416    if (enableProfiling) {417      markTaskStart(newTask, currentTime);418      newTask.isQueued = true;419    }420    // Schedule a host callback, if needed. If we're already performing work,421    // wait until the next time we yield.422    if (!isHostCallbackScheduled && !isPerformingWork) {423      isHostCallbackScheduled = true;424      requestHostCallback(flushWork);425    }426  }427428  return newTask;429}430431function unstable_cancelCallback(task: Task) {432  // $FlowFixMe[constant-condition]433  if (enableProfiling) {434    if (task.isQueued) {435      const currentTime = getCurrentTime();436      markTaskCanceled(task, currentTime);437      task.isQueued = false;438    }439  }440441  // Null out the callback to indicate the task has been canceled. (Can't442  // remove from the queue because you can't remove arbitrary nodes from an443  // array based heap, only the first one.)444  task.callback = null;445}446447function unstable_getCurrentPriorityLevel(): PriorityLevel {448  return currentPriorityLevel;449}450451function requestHostCallback(callback: (boolean, number) => boolean) {452  scheduledCallback = callback;453}454455function requestHostTimeout(callback: number => void, ms: number) {456  scheduledTimeout = callback;457  timeoutTime = currentMockTime + ms;458}459460function cancelHostTimeout(): void {461  scheduledTimeout = null;462  timeoutTime = -1;463}464465function shouldYieldToHost(): boolean {466  if (467    (expectedNumberOfYields === 0 && yieldedValues === null) ||468    (expectedNumberOfYields !== -1 &&469      yieldedValues !== null &&470      yieldedValues.length >= expectedNumberOfYields) ||471    (shouldYieldForPaint && needsPaint)472  ) {473    // We yielded at least as many values as expected. Stop flushing.474    didStop = true;475    return true;476  }477  return false;478}479480function getCurrentTime(): number {481  return currentMockTime;482}483484function forceFrameRate() {485  // No-op486}487488function reset() {489  if (isFlushing) {490    throw new Error('Cannot reset while already flushing work.');491  }492  currentMockTime = 0;493  scheduledCallback = null;494  scheduledTimeout = null;495  timeoutTime = -1;496  yieldedValues = null;497  expectedNumberOfYields = -1;498  didStop = false;499  isFlushing = false;500  needsPaint = false;501}502503// Should only be used via an assertion helper that inspects the yielded values.504function unstable_flushNumberOfYields(count: number): void {505  if (isFlushing) {506    throw new Error('Already flushing work.');507  }508  if (scheduledCallback !== null) {509    const cb = scheduledCallback;510    expectedNumberOfYields = count;511    isFlushing = true;512    try {513      let hasMoreWork = true;514      do {515        hasMoreWork = cb(true, currentMockTime);516      } while (hasMoreWork && !didStop);517      if (!hasMoreWork) {518        scheduledCallback = null;519      }520    } finally {521      expectedNumberOfYields = -1;522      didStop = false;523      isFlushing = false;524    }525  }526}527528function unstable_flushUntilNextPaint(): false {529  if (isFlushing) {530    throw new Error('Already flushing work.');531  }532  if (scheduledCallback !== null) {533    const cb = scheduledCallback;534    shouldYieldForPaint = true;535    needsPaint = false;536    isFlushing = true;537    try {538      let hasMoreWork = true;539      do {540        hasMoreWork = cb(true, currentMockTime);541      } while (hasMoreWork && !didStop);542      if (!hasMoreWork) {543        scheduledCallback = null;544      }545    } finally {546      shouldYieldForPaint = false;547      didStop = false;548      isFlushing = false;549    }550  }551  return false;552}553554function unstable_hasPendingWork(): boolean {555  return scheduledCallback !== null;556}557558function unstable_flushExpired() {559  if (isFlushing) {560    throw new Error('Already flushing work.');561  }562  if (scheduledCallback !== null) {563    isFlushing = true;564    try {565      const hasMoreWork = scheduledCallback(false, currentMockTime);566      if (!hasMoreWork) {567        scheduledCallback = null;568      }569    } finally {570      isFlushing = false;571    }572  }573}574575function unstable_flushAllWithoutAsserting(): boolean {576  // Returns false if no work was flushed.577  if (isFlushing) {578    throw new Error('Already flushing work.');579  }580  if (scheduledCallback !== null) {581    const cb = scheduledCallback;582    isFlushing = true;583    try {584      let hasMoreWork = true;585      do {586        hasMoreWork = cb(true, currentMockTime);587      } while (hasMoreWork);588      // $FlowFixMe[constant-condition]589      if (!hasMoreWork) {590        scheduledCallback = null;591      }592      return true;593    } finally {594      isFlushing = false;595    }596  } else {597    return false;598  }599}600601function unstable_clearLog(): Array<mixed> {602  if (yieldedValues === null) {603    return [];604  }605  const values = yieldedValues;606  yieldedValues = null;607  return values;608}609610function unstable_flushAll(): void {611  if (yieldedValues !== null) {612    throw new Error(613      'Log is not empty. Assert on the log of yielded values before ' +614        'flushing additional work.',615    );616  }617  unstable_flushAllWithoutAsserting();618  if (yieldedValues !== null) {619    throw new Error(620      'While flushing work, something yielded a value. Use an ' +621        'assertion helper to assert on the log of yielded values, e.g. ' +622        'expect(Scheduler).toFlushAndYield([...])',623    );624  }625}626627function log(value: mixed): void {628  // eslint-disable-next-line react-internal/no-production-logging629  if (console.log.name === 'disabledLog' || disableYieldValue) {630    // If console.log has been patched, we assume we're in render631    // replaying and we ignore any values yielding in the second pass.632    return;633  }634  if (yieldedValues === null) {635    yieldedValues = [value];636  } else {637    yieldedValues.push(value);638  }639}640641function unstable_advanceTime(ms: number) {642  // eslint-disable-next-line react-internal/no-production-logging643  if (console.log.name === 'disabledLog' || disableYieldValue) {644    // If console.log has been patched, we assume we're in render645    // replaying and we ignore any time advancing in the second pass.646    return;647  }648  currentMockTime += ms;649  if (scheduledTimeout !== null && timeoutTime <= currentMockTime) {650    scheduledTimeout(currentMockTime);651    timeoutTime = -1;652    scheduledTimeout = null;653  }654}655656function requestPaint() {657  needsPaint = true;658}659660export {661  ImmediatePriority as unstable_ImmediatePriority,662  UserBlockingPriority as unstable_UserBlockingPriority,663  NormalPriority as unstable_NormalPriority,664  IdlePriority as unstable_IdlePriority,665  LowPriority as unstable_LowPriority,666  unstable_runWithPriority,667  unstable_next,668  unstable_scheduleCallback,669  unstable_cancelCallback,670  unstable_wrapCallback,671  unstable_getCurrentPriorityLevel,672  shouldYieldToHost as unstable_shouldYield,673  requestPaint as unstable_requestPaint,674  getCurrentTime as unstable_now,675  forceFrameRate as unstable_forceFrameRate,676  unstable_flushAllWithoutAsserting,677  unstable_flushNumberOfYields,678  unstable_flushExpired,679  unstable_clearLog,680  unstable_flushUntilNextPaint,681  unstable_hasPendingWork,682  unstable_flushAll,683  log,684  unstable_advanceTime,685  reset,686  setDisableYieldValue as unstable_setDisableYieldValue,687};688689export const unstable_Profiling: {690  startLoggingProfilingEvents(): void,691  stopLoggingProfilingEvents(): ArrayBuffer | null,692  // $FlowFixMe[constant-condition]693} | null = enableProfiling694  ? {695      startLoggingProfilingEvents,696      stopLoggingProfilingEvents,697    }698  : null;

Code quality findings 61

Use let or const to avoid scope issues and hoisting
info correctness var-declaration
/* eslint-disable no-var */
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var maxSigned31BitInt = 1073741823;
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var IMMEDIATE_PRIORITY_TIMEOUT = -1;
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var USER_BLOCKING_PRIORITY_TIMEOUT = 250;
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var NORMAL_PRIORITY_TIMEOUT = 5000;
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var LOW_PRIORITY_TIMEOUT = 10000;
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var IDLE_PRIORITY_TIMEOUT = maxSigned31BitInt;
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var taskQueue: Array<Task> = [];
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var timerQueue: Array<Task> = [];
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var taskIdCounter = 1;
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var currentTask = null;
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var currentPriorityLevel: PriorityLevel = NormalPriority;
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var isPerformingWork = false;
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var isHostCallbackScheduled = false;
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var isHostTimeoutScheduled = false;
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var disableYieldValue = false;
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
while (timer !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (timer.callback === null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (peek(taskQueue) !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (firstTimer !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (currentTask !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
while (currentTask !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (typeof callback === 'function') {
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
if (typeof callback === 'function') {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (typeof continuationCallback === 'function') {
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
if (typeof continuationCallback === 'function') {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (currentTask === peek(taskQueue)) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (currentTask !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (firstTimer !== null) {
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var previousPriorityLevel = currentPriorityLevel;
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var priorityLevel: PriorityLevel;
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var previousPriorityLevel = currentPriorityLevel;
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var parentPriorityLevel = currentPriorityLevel;
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var previousPriorityLevel = currentPriorityLevel;
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var currentTime = getCurrentTime();
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var startTime;
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (typeof options === 'object' && options !== null) {
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
if (typeof options === 'object' && options !== null) {
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var delay = options.delay;
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (typeof delay === 'number' && delay > 0) {
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
if (typeof delay === 'number' && delay > 0) {
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var timeout;
Ensure all cases are handled or a default case is present
info correctness switch-without-default
switch (priorityLevel) {
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var expirationTime = startTime + timeout;
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var newTask: Task = {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (peek(taskQueue) === null && newTask === peek(timerQueue)) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
(expectedNumberOfYields === 0 && yieldedValues === null) ||
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
(expectedNumberOfYields !== -1 &&
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
yieldedValues !== null &&
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (scheduledCallback !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (scheduledCallback !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
return scheduledCallback !== null;
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (scheduledCallback !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (scheduledCallback !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (yieldedValues === null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (yieldedValues !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (yieldedValues !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (console.log.name === 'disabledLog' || disableYieldValue) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (yieldedValues === null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (console.log.name === 'disabledLog' || disableYieldValue) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (scheduledTimeout !== null && timeoutTime <= currentMockTime) {

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.