mirror of
https://github.com/facebook/react.git
synced 2026-02-24 04:33:04 +00:00
* First pass at scaffolding out the Node implementation of react-data. While incomplete, this patch contains some changes to the react-data package in order to start adding support for Node. The first part of this change accounts for splitting react-data/fetch into two discrete entries, adding (and defaulting to) the Node implementation. The second part is sketching out a rough approximation of `fetch` for Node. This implementation is not complete by any means, but provides a starting point. * Remove NodeFetch module and put it directly into ReactDataFetchNode. * Replaced react-data with react-fetch. This patch shuffles around some of the scaffolding that was in react-data in favor of react-fetch. It also removes the additional "fetch" package in favor of something flatter. * Tweak package organization * Simplify and add a test Co-authored-by: Dan Abramov <dan.abramov@me.com>
154 lines
3.5 KiB
JavaScript
154 lines
3.5 KiB
JavaScript
/**
|
|
* 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 type {Wakeable} from 'shared/ReactTypes';
|
|
|
|
import {readCache} from 'react/unstable-cache';
|
|
|
|
const Pending = 0;
|
|
const Resolved = 1;
|
|
const Rejected = 2;
|
|
|
|
type PendingResult = {|
|
|
status: 0,
|
|
value: Wakeable,
|
|
|};
|
|
|
|
type ResolvedResult = {|
|
|
status: 1,
|
|
value: mixed,
|
|
|};
|
|
|
|
type RejectedResult = {|
|
|
status: 2,
|
|
value: mixed,
|
|
|};
|
|
|
|
type Result = PendingResult | ResolvedResult | RejectedResult;
|
|
|
|
// TODO: this is a browser-only version. Add a separate Node entry point.
|
|
const nativeFetch = window.fetch;
|
|
const fetchKey = {};
|
|
|
|
function readResultMap(): Map<string, Result> {
|
|
const resources = readCache().resources;
|
|
let map = resources.get(fetchKey);
|
|
if (map === undefined) {
|
|
map = new Map();
|
|
resources.set(fetchKey, map);
|
|
}
|
|
return map;
|
|
}
|
|
|
|
function toResult(thenable): Result {
|
|
const result: Result = {
|
|
status: Pending,
|
|
value: thenable,
|
|
};
|
|
thenable.then(
|
|
value => {
|
|
if (result.status === Pending) {
|
|
const resolvedResult = ((result: any): ResolvedResult);
|
|
resolvedResult.status = Resolved;
|
|
resolvedResult.value = value;
|
|
}
|
|
},
|
|
err => {
|
|
if (result.status === Pending) {
|
|
const rejectedResult = ((result: any): RejectedResult);
|
|
rejectedResult.status = Rejected;
|
|
rejectedResult.value = err;
|
|
}
|
|
},
|
|
);
|
|
return result;
|
|
}
|
|
|
|
function readResult(result: Result) {
|
|
if (result.status === Resolved) {
|
|
return result.value;
|
|
} else {
|
|
throw result.value;
|
|
}
|
|
}
|
|
|
|
function Response(nativeResponse) {
|
|
this.headers = nativeResponse.headers;
|
|
this.ok = nativeResponse.ok;
|
|
this.redirected = nativeResponse.redirected;
|
|
this.status = nativeResponse.status;
|
|
this.statusText = nativeResponse.statusText;
|
|
this.type = nativeResponse.type;
|
|
this.url = nativeResponse.url;
|
|
|
|
this._response = nativeResponse;
|
|
this._arrayBuffer = null;
|
|
this._blob = null;
|
|
this._json = null;
|
|
this._text = null;
|
|
}
|
|
|
|
Response.prototype = {
|
|
constructor: Response,
|
|
arrayBuffer() {
|
|
return readResult(
|
|
this._arrayBuffer ||
|
|
(this._arrayBuffer = toResult(this._response.arrayBuffer())),
|
|
);
|
|
},
|
|
blob() {
|
|
return readResult(
|
|
this._blob || (this._blob = toResult(this._response.blob())),
|
|
);
|
|
},
|
|
json() {
|
|
return readResult(
|
|
this._json || (this._json = toResult(this._response.json())),
|
|
);
|
|
},
|
|
text() {
|
|
return readResult(
|
|
this._text || (this._text = toResult(this._response.text())),
|
|
);
|
|
},
|
|
};
|
|
|
|
function preloadResult(url: string, options: mixed): Result {
|
|
const map = readResultMap();
|
|
let entry = map.get(url);
|
|
if (!entry) {
|
|
if (options) {
|
|
if (options.method || options.body || options.signal) {
|
|
// TODO: wire up our own cancellation mechanism.
|
|
// TODO: figure out what to do with POST.
|
|
throw Error('Unsupported option');
|
|
}
|
|
}
|
|
const thenable = nativeFetch(url, options);
|
|
entry = toResult(thenable);
|
|
map.set(url, entry);
|
|
}
|
|
return entry;
|
|
}
|
|
|
|
export function preload(url: string, options: mixed): void {
|
|
preloadResult(url, options);
|
|
// Don't return anything.
|
|
}
|
|
|
|
export function fetch(url: string, options: mixed): Object {
|
|
const result = preloadResult(url, options);
|
|
const nativeResponse = (readResult(result): any);
|
|
if (nativeResponse._reactResponse) {
|
|
return nativeResponse._reactResponse;
|
|
} else {
|
|
return (nativeResponse._reactResponse = new Response(nativeResponse));
|
|
}
|
|
}
|