Skip to content

Commit

Permalink
Add fallback shim for AbortController
Browse files Browse the repository at this point in the history
  • Loading branch information
rickhanlonii committed Apr 6, 2022
1 parent ebd7ff6 commit 646ebad
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
// Polyfills for test environment
global.ReadableStream = require('web-streams-polyfill/ponyfill/es6').ReadableStream;
global.TextEncoder = require('util').TextEncoder;
global.AbortController = require('abort-controller');

let React;
let ReactDOMFizzServer;
Expand Down
138 changes: 138 additions & 0 deletions packages/react-reconciler/src/ReactAbortController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

import {enableCache} from 'shared/ReactFeatureFlags';

const PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map;

const signals: Map<
AbortControllerShim,
AbortSignalShim,
> = (new PossiblyWeakMap(): any);

const abortedFlags: Map<
AbortSignalShim,
boolean,
> = (new PossiblyWeakMap(): any);

type Listener = {
listener: any => void,
next: Listener | null,
};

const listenersMap: Map<
AbortSignalShim,
Map<string, Listener>,
> = (new PossiblyWeakMap(): any);

function getListeners(target): Map<string, Listener> {
const listeners = listenersMap.get(target);
if (listeners == null) {
// eslint-disable-next-line react-internal/prod-error-codes
throw new Error(
'Listeners should already be registered. This is a bug in React.',
);
}
return listeners;
}

class AbortSignalShim {
constructor() {
listenersMap.set(this, new Map<string, any>());
}
get aborted() {
return !!abortedFlags.get(this);
}

addEventListener(type, listener) {
const listeners = getListeners(this);
let node = listeners.get(type);

const newNode = {listener, next: null};
if (node === undefined) {
listeners.set(type, newNode);
return;
}

let prev = null;
while (node != null) {
prev = node;
node = node.next;
}
if (prev != null) {
prev.next = newNode;
}
}

removeEventListener(eventName, listener) {
const listeners = getListeners(this);
let node = listeners.get(eventName);

let prev = null;
while (node != null) {
if (node.listener === listener) {
if (prev !== null) {
prev.next = node.next;
} else if (node.next !== null) {
listeners.set(eventName, node.next);
} else {
listeners.delete(eventName);
}
return;
}

prev = node;
node = node.next;
}
}

dispatchEvent(event) {
const listeners = getListeners(this);
let node = listeners.get(event.type);
while (node != null) {
node.listener(event);
node = node.next;
}
}
}

class AbortControllerShim {
constructor() {
const signal = new AbortSignalShim();
abortedFlags.set(signal, false);
signals.set(this, signal);
}

get signal() {
return signals.get(this);
}

abort() {
const signal = signals.get(this);
if (signal == null) {
// eslint-disable-next-line react-internal/prod-error-codes
throw new Error(
'Aborted signal was not registered. This is a bug in React.',
);
}
if (abortedFlags.get(signal) !== false) {
return;
}
abortedFlags.set(signal, true);
signal.dispatchEvent({type: 'abort'});
}
}

// In environments without AbortController (e.g. tests)
// replace it with a lightweight shim that only has the features we use.
export default enableCache
? typeof AbortController !== 'undefined'
? AbortController
: (AbortControllerShim: AbortController)
: (null: any);
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type {ReactContext} from 'shared/ReactTypes';

import {enableCache} from 'shared/ReactFeatureFlags';
import {REACT_CONTEXT_TYPE} from 'shared/ReactSymbols';
import AbortController from './ReactAbortController';

import {pushProvider, popProvider} from './ReactFiberNewContext.new';
import * as Scheduler from 'scheduler';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type {ReactContext} from 'shared/ReactTypes';

import {enableCache} from 'shared/ReactFeatureFlags';
import {REACT_CONTEXT_TYPE} from 'shared/ReactSymbols';
import AbortController from './ReactAbortController';

import {pushProvider, popProvider} from './ReactFiberNewContext.old';
import * as Scheduler from 'scheduler';
Expand Down
4 changes: 0 additions & 4 deletions scripts/jest/setupEnvironment.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
/* eslint-disable */

const AbortController = require('abort-controller');

const NODE_ENV = process.env.NODE_ENV;
if (NODE_ENV !== 'development' && NODE_ENV !== 'production') {
throw new Error('NODE_ENV must either be set to development or production.');
Expand All @@ -23,8 +21,6 @@ global.__EXPERIMENTAL__ =

global.__VARIANT__ = !!process.env.VARIANT;

global.AbortController = AbortController;

if (typeof window !== 'undefined') {
global.requestIdleCallback = function(callback) {
return setTimeout(() => {
Expand Down

0 comments on commit 646ebad

Please sign in to comment.