mirror of
https://github.com/zebrajr/react.git
synced 2025-12-06 00:20:04 +01:00
Remove old react-fetch, react-fs and react-pg libraries (#25577)
To avoid confusion. We are patching `fetch`, and only `fetch`, for a small fix scoped to react renders elsewhere, but this code is not it. This code was for the strategy used in the original [React Server Components demo](https://github.com/reactjs/server-components-demo). Which [we announced](https://reactjs.org/blog/2022/06/15/react-labs-what-we-have-been-working-on-june-2022.html) that we're moving away from in favor of [First class support for promises and async/await](https://github.com/reactjs/rfcs/pull/229). We might explore using these package for other instrumentation in the future but not now and not like this.
This commit is contained in:
parent
28a574ea8f
commit
cf3932be5c
|
|
@ -140,8 +140,6 @@ module.exports = {
|
|||
'packages/react-dom/src/test-utils/**/*.js',
|
||||
'packages/react-devtools-shared/**/*.js',
|
||||
'packages/react-noop-renderer/**/*.js',
|
||||
'packages/react-pg/**/*.js',
|
||||
'packages/react-fs/**/*.js',
|
||||
'packages/react-refresh/**/*.js',
|
||||
'packages/react-server-dom-webpack/**/*.js',
|
||||
'packages/react-test-renderer/**/*.js',
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ const stablePackages = {
|
|||
// These packages do not exist in the @next or @latest channel, only
|
||||
// @experimental. We don't use semver, just the commit sha, so this is just a
|
||||
// list of package names instead of a map.
|
||||
const experimentalPackages = ['react-fetch', 'react-fs', 'react-pg'];
|
||||
const experimentalPackages = [];
|
||||
|
||||
module.exports = {
|
||||
ReactVersion,
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
# react-fetch
|
||||
|
||||
This package is meant to be used alongside yet-to-be-released, experimental React features. It's unlikely to be useful in any other context.
|
||||
|
||||
**Do not use in a real application.** We're publishing this early for
|
||||
demonstration purposes.
|
||||
|
||||
**Use it at your own risk.**
|
||||
|
||||
# No, Really, It Is Unstable
|
||||
|
||||
The API ~~may~~ will change wildly between versions.
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
export * from './src/ReactFetchBrowser';
|
||||
12
packages/react-fetch/index.js
vendored
12
packages/react-fetch/index.js
vendored
|
|
@ -1,12 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
export * from './index.node';
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
export * from './src/ReactFetchNode';
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
module.exports = require('./cjs/react-fetch.browser.production.min.js');
|
||||
} else {
|
||||
module.exports = require('./cjs/react-fetch.browser.development.js');
|
||||
}
|
||||
3
packages/react-fetch/npm/index.js
vendored
3
packages/react-fetch/npm/index.js
vendored
|
|
@ -1,3 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
module.exports = require('./index.node');
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
module.exports = require('./cjs/react-fetch.node.production.min.js');
|
||||
} else {
|
||||
module.exports = require('./cjs/react-fetch.node.development.js');
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
{
|
||||
"name": "react-fetch",
|
||||
"description": "Helpers for creating React data sources",
|
||||
"version": "0.0.0",
|
||||
"repository": {
|
||||
"type" : "git",
|
||||
"url" : "https://github.com/facebook/react.git",
|
||||
"directory": "packages/react-fetch"
|
||||
},
|
||||
"files": [
|
||||
"LICENSE",
|
||||
"README.md",
|
||||
"index.js",
|
||||
"index.node.js",
|
||||
"index.browser.js",
|
||||
"cjs/"
|
||||
],
|
||||
"peerDependencies": {
|
||||
"react": "^17.0.0"
|
||||
},
|
||||
"browser": {
|
||||
"./index.js": "./index.browser.js"
|
||||
}
|
||||
}
|
||||
169
packages/react-fetch/src/ReactFetchBrowser.js
vendored
169
packages/react-fetch/src/ReactFetchBrowser.js
vendored
|
|
@ -1,169 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and 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 {unstable_getCacheForType} from 'react';
|
||||
|
||||
const Pending = 0;
|
||||
const Resolved = 1;
|
||||
const Rejected = 2;
|
||||
|
||||
type PendingRecord = {
|
||||
status: 0,
|
||||
value: Wakeable,
|
||||
};
|
||||
|
||||
type ResolvedRecord = {
|
||||
status: 1,
|
||||
value: mixed,
|
||||
};
|
||||
|
||||
type RejectedRecord = {
|
||||
status: 2,
|
||||
value: mixed,
|
||||
};
|
||||
|
||||
type Record = PendingRecord | ResolvedRecord | RejectedRecord;
|
||||
|
||||
declare var globalThis: any;
|
||||
|
||||
// TODO: this is a browser-only version. Add a separate Node entry point.
|
||||
const nativeFetch = (typeof globalThis !== 'undefined' ? globalThis : window)
|
||||
.fetch;
|
||||
|
||||
function getRecordMap(): Map<string, Record> {
|
||||
return unstable_getCacheForType(createRecordMap);
|
||||
}
|
||||
|
||||
function createRecordMap(): Map<string, Record> {
|
||||
return new Map();
|
||||
}
|
||||
|
||||
function createRecordFromThenable(thenable): Record {
|
||||
const record: Record = {
|
||||
status: Pending,
|
||||
value: thenable,
|
||||
};
|
||||
thenable.then(
|
||||
value => {
|
||||
if (record.status === Pending) {
|
||||
const resolvedRecord = ((record: any): ResolvedRecord);
|
||||
resolvedRecord.status = Resolved;
|
||||
resolvedRecord.value = value;
|
||||
}
|
||||
},
|
||||
err => {
|
||||
if (record.status === Pending) {
|
||||
const rejectedRecord = ((record: any): RejectedRecord);
|
||||
rejectedRecord.status = Rejected;
|
||||
rejectedRecord.value = err;
|
||||
}
|
||||
},
|
||||
);
|
||||
return record;
|
||||
}
|
||||
|
||||
function readRecordValue(record: Record) {
|
||||
if (record.status === Resolved) {
|
||||
return record.value;
|
||||
} else {
|
||||
throw record.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 readRecordValue(
|
||||
// $FlowFixMe[object-this-reference] found when upgrading Flow
|
||||
this._arrayBuffer ||
|
||||
// $FlowFixMe[object-this-reference] found when upgrading Flow
|
||||
(this._arrayBuffer = createRecordFromThenable(
|
||||
// $FlowFixMe[object-this-reference] found when upgrading Flow
|
||||
this._response.arrayBuffer(),
|
||||
)),
|
||||
);
|
||||
},
|
||||
blob() {
|
||||
return readRecordValue(
|
||||
// $FlowFixMe[object-this-reference] found when upgrading Flow
|
||||
this._blob ||
|
||||
// $FlowFixMe[object-this-reference] found when upgrading Flow
|
||||
(this._blob = createRecordFromThenable(this._response.blob())),
|
||||
);
|
||||
},
|
||||
json() {
|
||||
return readRecordValue(
|
||||
// $FlowFixMe[object-this-reference] found when upgrading Flow
|
||||
this._json ||
|
||||
// $FlowFixMe[object-this-reference] found when upgrading Flow
|
||||
(this._json = createRecordFromThenable(this._response.json())),
|
||||
);
|
||||
},
|
||||
text() {
|
||||
return readRecordValue(
|
||||
// $FlowFixMe[object-this-reference] found when upgrading Flow
|
||||
this._text ||
|
||||
// $FlowFixMe[object-this-reference] found when upgrading Flow
|
||||
(this._text = createRecordFromThenable(this._response.text())),
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
function preloadRecord(url: string, options: mixed): Record {
|
||||
const map = getRecordMap();
|
||||
let record = map.get(url);
|
||||
if (!record) {
|
||||
if (options) {
|
||||
if (options.method || options.body || options.signal) {
|
||||
// TODO: wire up our own cancellation mechanism.
|
||||
// TODO: figure out what to do with POST.
|
||||
// eslint-disable-next-line react-internal/prod-error-codes
|
||||
throw Error('Unsupported option');
|
||||
}
|
||||
}
|
||||
const thenable = nativeFetch(url, options);
|
||||
record = createRecordFromThenable(thenable);
|
||||
map.set(url, record);
|
||||
}
|
||||
return record;
|
||||
}
|
||||
|
||||
export function preload(url: string, options: mixed): void {
|
||||
preloadRecord(url, options);
|
||||
// Don't return anything.
|
||||
}
|
||||
|
||||
export function fetch(url: string, options: mixed): Object {
|
||||
const record = preloadRecord(url, options);
|
||||
const nativeResponse = (readRecordValue(record): any);
|
||||
if (nativeResponse._reactResponse) {
|
||||
return nativeResponse._reactResponse;
|
||||
} else {
|
||||
// $FlowFixMe[invalid-constructor] Flow no longer supports calling new on functions
|
||||
return (nativeResponse._reactResponse = new Response(nativeResponse));
|
||||
}
|
||||
}
|
||||
246
packages/react-fetch/src/ReactFetchNode.js
vendored
246
packages/react-fetch/src/ReactFetchNode.js
vendored
|
|
@ -1,246 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and 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 * as http from 'http';
|
||||
import * as https from 'https';
|
||||
import {unstable_getCacheForType} from 'react';
|
||||
|
||||
type FetchResponse = {
|
||||
// Properties
|
||||
headers: any,
|
||||
ok: boolean,
|
||||
redirected: boolean,
|
||||
status: number,
|
||||
statusText: string,
|
||||
type: 'basic',
|
||||
url: string,
|
||||
// Methods
|
||||
arrayBuffer(): ArrayBuffer,
|
||||
blob(): any,
|
||||
json(): any,
|
||||
text(): string,
|
||||
};
|
||||
|
||||
function nodeFetch(
|
||||
url: string,
|
||||
options: mixed,
|
||||
onResolve: any => void,
|
||||
onReject: any => void,
|
||||
): void {
|
||||
const {hostname, pathname, search, port, protocol} = new URL(url);
|
||||
const nodeOptions = {
|
||||
hostname,
|
||||
port,
|
||||
path: pathname + search,
|
||||
// TODO: cherry-pick supported user-passed options.
|
||||
};
|
||||
const nodeImpl = protocol === 'https:' ? https : http;
|
||||
// $FlowFixMe: node flow type has `port` as a number
|
||||
const request = nodeImpl.request(nodeOptions, response => {
|
||||
// TODO: support redirects.
|
||||
// $FlowFixMe[invalid-constructor] Flow no longer supports calling new on functions
|
||||
onResolve(new Response(response));
|
||||
});
|
||||
request.on('error', error => {
|
||||
onReject(error);
|
||||
});
|
||||
request.end();
|
||||
}
|
||||
|
||||
const Pending = 0;
|
||||
const Resolved = 1;
|
||||
const Rejected = 2;
|
||||
|
||||
type PendingRecord = {
|
||||
status: 0,
|
||||
value: Wakeable,
|
||||
};
|
||||
|
||||
type ResolvedRecord<V> = {
|
||||
status: 1,
|
||||
value: V,
|
||||
};
|
||||
|
||||
type RejectedRecord = {
|
||||
status: 2,
|
||||
value: mixed,
|
||||
};
|
||||
|
||||
type Record<V> = PendingRecord | ResolvedRecord<V> | RejectedRecord;
|
||||
|
||||
function getRecordMap(): Map<string, Record<FetchResponse>> {
|
||||
return unstable_getCacheForType(createRecordMap);
|
||||
}
|
||||
|
||||
function createRecordMap(): Map<string, Record<FetchResponse>> {
|
||||
return new Map();
|
||||
}
|
||||
|
||||
function readRecordValue<T>(record: Record<T>): T {
|
||||
if (record.status === Resolved) {
|
||||
return record.value;
|
||||
} else {
|
||||
throw record.value;
|
||||
}
|
||||
}
|
||||
|
||||
function Response(nativeResponse) {
|
||||
this.headers = nativeResponse.headers;
|
||||
this.ok = nativeResponse.statusCode >= 200 && nativeResponse.statusCode < 300;
|
||||
this.redirected = false; // TODO
|
||||
this.status = nativeResponse.statusCode;
|
||||
this.statusText = nativeResponse.statusMessage;
|
||||
this.type = 'basic';
|
||||
this.url = nativeResponse.url;
|
||||
|
||||
this._response = nativeResponse;
|
||||
this._json = null;
|
||||
this._text = null;
|
||||
|
||||
const callbacks = [];
|
||||
function wake() {
|
||||
// This assumes they won't throw.
|
||||
while (callbacks.length > 0) {
|
||||
const cb = callbacks.pop();
|
||||
cb();
|
||||
}
|
||||
}
|
||||
const bufferRecord: PendingRecord = (this._bufferRecord = {
|
||||
status: Pending,
|
||||
value: {
|
||||
then(cb) {
|
||||
callbacks.push(cb);
|
||||
},
|
||||
},
|
||||
});
|
||||
const data = [];
|
||||
nativeResponse.on('data', chunk => data.push(chunk));
|
||||
nativeResponse.on('end', () => {
|
||||
if (bufferRecord.status === Pending) {
|
||||
const resolvedRecord = ((bufferRecord: any): ResolvedRecord<Buffer>);
|
||||
resolvedRecord.status = Resolved;
|
||||
resolvedRecord.value = Buffer.concat(data);
|
||||
wake();
|
||||
}
|
||||
});
|
||||
nativeResponse.on('error', err => {
|
||||
if (bufferRecord.status === Pending) {
|
||||
const rejectedRecord = ((bufferRecord: any): RejectedRecord);
|
||||
rejectedRecord.status = Rejected;
|
||||
rejectedRecord.value = err;
|
||||
wake();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Response.prototype = {
|
||||
constructor: Response,
|
||||
arrayBuffer() {
|
||||
// $FlowFixMe[object-this-reference] found when upgrading Flow
|
||||
const buffer = readRecordValue(this._bufferRecord);
|
||||
return buffer;
|
||||
},
|
||||
blob() {
|
||||
// TODO: Is this needed?
|
||||
throw new Error('Not implemented.');
|
||||
},
|
||||
json() {
|
||||
// $FlowFixMe[object-this-reference] found when upgrading Flow
|
||||
if (this._json !== null) {
|
||||
// $FlowFixMe[object-this-reference] found when upgrading Flow
|
||||
return this._json;
|
||||
}
|
||||
// $FlowFixMe[object-this-reference] found when upgrading Flow
|
||||
const buffer = readRecordValue(this._bufferRecord);
|
||||
const json = JSON.parse(buffer.toString());
|
||||
// $FlowFixMe[object-this-reference] found when upgrading Flow
|
||||
this._json = json;
|
||||
return json;
|
||||
},
|
||||
text() {
|
||||
// $FlowFixMe[object-this-reference] found when upgrading Flow
|
||||
if (this._text !== null) {
|
||||
// $FlowFixMe[object-this-reference] found when upgrading Flow
|
||||
return this._text;
|
||||
}
|
||||
// $FlowFixMe[object-this-reference] found when upgrading Flow
|
||||
const buffer = readRecordValue(this._bufferRecord);
|
||||
const text = buffer.toString();
|
||||
// $FlowFixMe[object-this-reference] found when upgrading Flow
|
||||
this._text = text;
|
||||
return text;
|
||||
},
|
||||
};
|
||||
|
||||
function preloadRecord(url: string, options: mixed): Record<FetchResponse> {
|
||||
const map = getRecordMap();
|
||||
let record = map.get(url);
|
||||
if (!record) {
|
||||
if (options) {
|
||||
if (options.method || options.body || options.signal) {
|
||||
// TODO: wire up our own cancellation mechanism.
|
||||
// TODO: figure out what to do with POST.
|
||||
// eslint-disable-next-line react-internal/prod-error-codes
|
||||
throw Error('Unsupported option');
|
||||
}
|
||||
}
|
||||
const callbacks = [];
|
||||
const wakeable = {
|
||||
then(cb) {
|
||||
callbacks.push(cb);
|
||||
},
|
||||
};
|
||||
const wake = () => {
|
||||
// This assumes they won't throw.
|
||||
while (callbacks.length > 0) {
|
||||
const cb = callbacks.pop();
|
||||
cb();
|
||||
}
|
||||
};
|
||||
const newRecord: Record<FetchResponse> = (record = {
|
||||
status: Pending,
|
||||
value: wakeable,
|
||||
});
|
||||
nodeFetch(
|
||||
url,
|
||||
options,
|
||||
response => {
|
||||
if (newRecord.status === Pending) {
|
||||
const resolvedRecord = ((newRecord: any): ResolvedRecord<FetchResponse>);
|
||||
resolvedRecord.status = Resolved;
|
||||
resolvedRecord.value = response;
|
||||
wake();
|
||||
}
|
||||
},
|
||||
err => {
|
||||
if (newRecord.status === Pending) {
|
||||
const rejectedRecord = ((newRecord: any): RejectedRecord);
|
||||
rejectedRecord.status = Rejected;
|
||||
rejectedRecord.value = err;
|
||||
wake();
|
||||
}
|
||||
},
|
||||
);
|
||||
map.set(url, record);
|
||||
}
|
||||
return record;
|
||||
}
|
||||
|
||||
export function preload(url: string, options: mixed): void {
|
||||
preloadRecord(url, options);
|
||||
// Don't return anything.
|
||||
}
|
||||
|
||||
export function fetch(url: string, options: mixed): FetchResponse {
|
||||
const record = preloadRecord(url, options);
|
||||
const response = readRecordValue(record);
|
||||
return response;
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @emails react-core
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
describe('ReactFetchBrowser', () => {
|
||||
let ReactFetchBrowser;
|
||||
|
||||
beforeEach(() => {
|
||||
if (__EXPERIMENTAL__) {
|
||||
ReactFetchBrowser = require('react-fetch');
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: test something useful.
|
||||
// @gate experimental
|
||||
it('exports something', () => {
|
||||
expect(ReactFetchBrowser.fetch).not.toBe(undefined);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,121 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @emails react-core
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
describe('ReactFetchNode', () => {
|
||||
let http;
|
||||
let fetch;
|
||||
let waitForSuspense;
|
||||
let server;
|
||||
let serverEndpoint;
|
||||
let serverImpl;
|
||||
|
||||
beforeEach(done => {
|
||||
jest.resetModules();
|
||||
|
||||
fetch = require('react-fetch').fetch;
|
||||
http = require('http');
|
||||
waitForSuspense = require('react-suspense-test-utils').waitForSuspense;
|
||||
|
||||
server = http.createServer((req, res) => {
|
||||
serverImpl(req, res);
|
||||
});
|
||||
serverEndpoint = null;
|
||||
server.listen(() => {
|
||||
serverEndpoint = `http://localhost:${server.address().port}/`;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(done => {
|
||||
server.close(done);
|
||||
server = null;
|
||||
});
|
||||
|
||||
// @gate experimental || www
|
||||
it('can fetch text from a server component', async () => {
|
||||
serverImpl = (req, res) => {
|
||||
res.write('mango');
|
||||
res.end();
|
||||
};
|
||||
const text = await waitForSuspense(() => {
|
||||
return fetch(serverEndpoint).text();
|
||||
});
|
||||
expect(text).toEqual('mango');
|
||||
});
|
||||
|
||||
// @gate experimental || www
|
||||
it('can fetch json from a server component', async () => {
|
||||
serverImpl = (req, res) => {
|
||||
res.write(JSON.stringify({name: 'Sema'}));
|
||||
res.end();
|
||||
};
|
||||
const json = await waitForSuspense(() => {
|
||||
return fetch(serverEndpoint).json();
|
||||
});
|
||||
expect(json).toEqual({name: 'Sema'});
|
||||
});
|
||||
|
||||
// @gate experimental || www
|
||||
it('provides response status', async () => {
|
||||
serverImpl = (req, res) => {
|
||||
res.write(JSON.stringify({name: 'Sema'}));
|
||||
res.end();
|
||||
};
|
||||
const response = await waitForSuspense(() => {
|
||||
return fetch(serverEndpoint);
|
||||
});
|
||||
expect(response).toMatchObject({
|
||||
status: 200,
|
||||
statusText: 'OK',
|
||||
ok: true,
|
||||
});
|
||||
});
|
||||
|
||||
// @gate experimental || www
|
||||
it('handles different paths', async () => {
|
||||
serverImpl = (req, res) => {
|
||||
switch (req.url) {
|
||||
case '/banana':
|
||||
res.write('banana');
|
||||
break;
|
||||
case '/mango':
|
||||
res.write('mango');
|
||||
break;
|
||||
case '/orange':
|
||||
res.write('orange');
|
||||
break;
|
||||
}
|
||||
res.end();
|
||||
};
|
||||
const outputs = await waitForSuspense(() => {
|
||||
return [
|
||||
fetch(serverEndpoint + 'banana').text(),
|
||||
fetch(serverEndpoint + 'mango').text(),
|
||||
fetch(serverEndpoint + 'orange').text(),
|
||||
];
|
||||
});
|
||||
expect(outputs).toMatchObject(['banana', 'mango', 'orange']);
|
||||
});
|
||||
|
||||
// @gate experimental || www
|
||||
it('can produce an error', async () => {
|
||||
serverImpl = (req, res) => {};
|
||||
|
||||
expect.assertions(1);
|
||||
try {
|
||||
await waitForSuspense(() => {
|
||||
return fetch('BOOM');
|
||||
});
|
||||
} catch (err) {
|
||||
expect(err.message).toEqual('Invalid URL: BOOM');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
# react-fs
|
||||
|
||||
This package is meant to be used alongside yet-to-be-released, experimental React features. It's unlikely to be useful in any other context.
|
||||
|
||||
**Do not use in a real application.** We're publishing this early for
|
||||
demonstration purposes.
|
||||
|
||||
**Use it at your own risk.**
|
||||
|
||||
# No, Really, It Is Unstable
|
||||
|
||||
The API ~~may~~ will change wildly between versions.
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
throw new Error(
|
||||
'This entry point is not yet supported in the browser environment',
|
||||
);
|
||||
13
packages/react-fs/index.js
vendored
13
packages/react-fs/index.js
vendored
|
|
@ -1,13 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
throw new Error(
|
||||
'React FS cannot be used outside a react-server environment. ' +
|
||||
'You must configure Node.js using the `--conditions react-server` flag.',
|
||||
);
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
export * from './src/ReactFilesystem';
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
module.exports = require('./cjs/react-fs.browser.production.min.server.js');
|
||||
} else {
|
||||
module.exports = require('./cjs/react-fs.browser.development.server.js');
|
||||
}
|
||||
6
packages/react-fs/npm/index.js
vendored
6
packages/react-fs/npm/index.js
vendored
|
|
@ -1,6 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
throw new Error(
|
||||
'React FS cannot be used outside a react-server environment. ' +
|
||||
'You must configure Node.js using the `--conditions react-server` flag.'
|
||||
);
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
module.exports = require('./cjs/react-fs.node.production.min.server.js');
|
||||
} else {
|
||||
module.exports = require('./cjs/react-fs.node.development.server.js');
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
{
|
||||
"name": "react-fs",
|
||||
"description": "React bindings for the filesystem",
|
||||
"version": "0.0.0",
|
||||
"repository": {
|
||||
"type" : "git",
|
||||
"url" : "https://github.com/facebook/react.git",
|
||||
"directory": "packages/react-fs"
|
||||
},
|
||||
"files": [
|
||||
"LICENSE",
|
||||
"README.md",
|
||||
"index.js",
|
||||
"index.node.server.js",
|
||||
"index.browser.server.js",
|
||||
"cjs/"
|
||||
],
|
||||
"exports": {
|
||||
".": {
|
||||
"react-server": {
|
||||
"node": "./index.node.server.js",
|
||||
"browser": "./index.browser.server.js"
|
||||
},
|
||||
"default": "./index.js"
|
||||
},
|
||||
"./index.node.server": "./index.node.server.js",
|
||||
"./index.browser.server": "./index.browser.server.js",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^17.0.0"
|
||||
}
|
||||
}
|
||||
391
packages/react-fs/src/ReactFilesystem.js
vendored
391
packages/react-fs/src/ReactFilesystem.js
vendored
|
|
@ -1,391 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and 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, Thenable} from 'shared/ReactTypes';
|
||||
|
||||
import {unstable_getCacheForType} from 'react';
|
||||
import * as fs from 'fs/promises';
|
||||
import {isAbsolute, normalize} from 'path';
|
||||
|
||||
const Pending = 0;
|
||||
const Resolved = 1;
|
||||
const Rejected = 2;
|
||||
|
||||
type PendingRecord = {
|
||||
status: 0,
|
||||
value: Wakeable,
|
||||
cache: null,
|
||||
};
|
||||
|
||||
type ResolvedRecord<T> = {
|
||||
status: 1,
|
||||
value: T,
|
||||
cache: null | Array<mixed>,
|
||||
};
|
||||
|
||||
type RejectedRecord = {
|
||||
status: 2,
|
||||
value: mixed,
|
||||
cache: null,
|
||||
};
|
||||
|
||||
type Record<T> = PendingRecord | ResolvedRecord<T> | RejectedRecord;
|
||||
|
||||
function createRecordFromThenable<T>(thenable: Thenable<T>): Record<T> {
|
||||
const record: Record<T> = {
|
||||
status: Pending,
|
||||
value: thenable,
|
||||
cache: null,
|
||||
};
|
||||
thenable.then(
|
||||
value => {
|
||||
if (record.status === Pending) {
|
||||
const resolvedRecord = ((record: any): ResolvedRecord<T>);
|
||||
resolvedRecord.status = Resolved;
|
||||
resolvedRecord.value = value;
|
||||
}
|
||||
},
|
||||
err => {
|
||||
if (record.status === Pending) {
|
||||
const rejectedRecord = ((record: any): RejectedRecord);
|
||||
rejectedRecord.status = Rejected;
|
||||
rejectedRecord.value = err;
|
||||
}
|
||||
},
|
||||
);
|
||||
return record;
|
||||
}
|
||||
|
||||
function readRecord<T>(record: Record<T>): ResolvedRecord<T> {
|
||||
if (record.status === Resolved) {
|
||||
// This is just a type refinement.
|
||||
return record;
|
||||
} else {
|
||||
throw record.value;
|
||||
}
|
||||
}
|
||||
|
||||
// We don't want to normalize every path ourselves in production.
|
||||
// However, relative or non-normalized paths will lead to cache misses.
|
||||
// So we encourage the developer to fix it in DEV and normalize on their end.
|
||||
function checkPathInDev(path: string) {
|
||||
if (__DEV__) {
|
||||
if (!isAbsolute(path)) {
|
||||
console.error(
|
||||
'The provided path was not absolute: "%s". ' +
|
||||
'Convert it to an absolute path first.',
|
||||
path,
|
||||
);
|
||||
} else if (path !== normalize(path)) {
|
||||
console.error(
|
||||
'The provided path was not normalized: "%s". ' +
|
||||
'Convert it to a normalized path first.',
|
||||
path,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createAccessMap(): Map<string, Array<number | Record<void>>> {
|
||||
return new Map();
|
||||
}
|
||||
|
||||
export function access(path: string, mode?: number): void {
|
||||
checkPathInDev(path);
|
||||
if (mode == null) {
|
||||
mode = 0; // fs.constants.F_OK
|
||||
}
|
||||
const map = unstable_getCacheForType(createAccessMap);
|
||||
let accessCache = map.get(path);
|
||||
if (!accessCache) {
|
||||
accessCache = [];
|
||||
map.set(path, accessCache);
|
||||
}
|
||||
let record;
|
||||
for (let i = 0; i < accessCache.length; i += 2) {
|
||||
const cachedMode: number = (accessCache[i]: any);
|
||||
if (mode === cachedMode) {
|
||||
const cachedRecord: Record<void> = (accessCache[i + 1]: any);
|
||||
record = cachedRecord;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!record) {
|
||||
const thenable = fs.access(path, mode);
|
||||
record = createRecordFromThenable(thenable);
|
||||
accessCache.push(mode, record);
|
||||
}
|
||||
readRecord(record); // No return value.
|
||||
}
|
||||
|
||||
function createLstatMap(): Map<string, Array<boolean | Record<mixed>>> {
|
||||
return new Map();
|
||||
}
|
||||
|
||||
export function lstat(path: string, options?: {bigint?: boolean}): mixed {
|
||||
checkPathInDev(path);
|
||||
let bigint = false;
|
||||
if (options && options.bigint) {
|
||||
bigint = true;
|
||||
}
|
||||
const map = unstable_getCacheForType(createLstatMap);
|
||||
let lstatCache = map.get(path);
|
||||
if (!lstatCache) {
|
||||
lstatCache = [];
|
||||
map.set(path, lstatCache);
|
||||
}
|
||||
let record;
|
||||
for (let i = 0; i < lstatCache.length; i += 2) {
|
||||
const cachedBigint: boolean = (lstatCache[i]: any);
|
||||
if (bigint === cachedBigint) {
|
||||
const cachedRecord: Record<void> = (lstatCache[i + 1]: any);
|
||||
record = cachedRecord;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!record) {
|
||||
const thenable = fs.lstat(path, {bigint});
|
||||
record = createRecordFromThenable(thenable);
|
||||
// $FlowFixMe[incompatible-call] found when upgrading Flow
|
||||
lstatCache.push(bigint, record);
|
||||
}
|
||||
// $FlowFixMe[incompatible-call] found when upgrading Flow
|
||||
const stats = readRecord(record).value;
|
||||
return stats;
|
||||
}
|
||||
|
||||
function createReaddirMap(): Map<
|
||||
string,
|
||||
Array<string | boolean | Record<mixed>>,
|
||||
> {
|
||||
return new Map();
|
||||
}
|
||||
|
||||
export function readdir(
|
||||
path: string,
|
||||
options?: string | {encoding?: string, withFileTypes?: boolean},
|
||||
): mixed {
|
||||
checkPathInDev(path);
|
||||
let encoding = 'utf8';
|
||||
let withFileTypes = false;
|
||||
if (typeof options === 'string') {
|
||||
encoding = options;
|
||||
} else if (options != null) {
|
||||
if (options.encoding) {
|
||||
encoding = options.encoding;
|
||||
}
|
||||
if (options.withFileTypes) {
|
||||
withFileTypes = true;
|
||||
}
|
||||
}
|
||||
const map = unstable_getCacheForType(createReaddirMap);
|
||||
let readdirCache = map.get(path);
|
||||
if (!readdirCache) {
|
||||
readdirCache = [];
|
||||
map.set(path, readdirCache);
|
||||
}
|
||||
let record;
|
||||
for (let i = 0; i < readdirCache.length; i += 3) {
|
||||
const cachedEncoding: string = (readdirCache[i]: any);
|
||||
const cachedWithFileTypes: boolean = (readdirCache[i + 1]: any);
|
||||
if (encoding === cachedEncoding && withFileTypes === cachedWithFileTypes) {
|
||||
const cachedRecord: Record<void> = (readdirCache[i + 2]: any);
|
||||
record = cachedRecord;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!record) {
|
||||
const thenable = fs.readdir(path, {encoding, withFileTypes});
|
||||
record = createRecordFromThenable(thenable);
|
||||
// $FlowFixMe[incompatible-call] found when upgrading Flow
|
||||
readdirCache.push(encoding, withFileTypes, record);
|
||||
}
|
||||
// $FlowFixMe[incompatible-call] found when upgrading Flow
|
||||
const files = readRecord(record).value;
|
||||
return files;
|
||||
}
|
||||
|
||||
function createReadFileMap(): Map<string, Record<Buffer>> {
|
||||
return new Map();
|
||||
}
|
||||
|
||||
export function readFile(
|
||||
path: string,
|
||||
options:
|
||||
| string
|
||||
| {
|
||||
encoding?: string | null,
|
||||
// Unsupported:
|
||||
flag?: string, // Doesn't make sense except "r"
|
||||
signal?: mixed, // We'll have our own signal
|
||||
},
|
||||
): string | Buffer {
|
||||
checkPathInDev(path);
|
||||
const map = unstable_getCacheForType(createReadFileMap);
|
||||
let record = map.get(path);
|
||||
if (!record) {
|
||||
const thenable = fs.readFile(path);
|
||||
record = createRecordFromThenable(thenable);
|
||||
map.set(path, record);
|
||||
}
|
||||
const resolvedRecord = readRecord(record);
|
||||
const buffer: Buffer = resolvedRecord.value;
|
||||
if (!options) {
|
||||
return buffer;
|
||||
}
|
||||
let encoding;
|
||||
if (typeof options === 'string') {
|
||||
encoding = options;
|
||||
} else {
|
||||
const flag = options.flag;
|
||||
if (flag != null && flag !== 'r') {
|
||||
throw Error(
|
||||
'The flag option is not supported, and always defaults to "r".',
|
||||
);
|
||||
}
|
||||
if (options.signal) {
|
||||
throw Error('The signal option is not supported.');
|
||||
}
|
||||
encoding = options.encoding;
|
||||
}
|
||||
if (typeof encoding !== 'string') {
|
||||
return buffer;
|
||||
}
|
||||
const textCache = resolvedRecord.cache || (resolvedRecord.cache = []);
|
||||
for (let i = 0; i < textCache.length; i += 2) {
|
||||
if (textCache[i] === encoding) {
|
||||
return (textCache[i + 1]: any);
|
||||
}
|
||||
}
|
||||
const text = buffer.toString((encoding: any));
|
||||
textCache.push(encoding, text);
|
||||
return text;
|
||||
}
|
||||
|
||||
function createReadlinkMap(): Map<string, Array<string | Record<mixed>>> {
|
||||
return new Map();
|
||||
}
|
||||
|
||||
export function readlink(
|
||||
path: string,
|
||||
options?: string | {encoding?: string},
|
||||
): mixed {
|
||||
checkPathInDev(path);
|
||||
let encoding = 'utf8';
|
||||
if (typeof options === 'string') {
|
||||
encoding = options;
|
||||
} else if (options != null) {
|
||||
if (options.encoding) {
|
||||
encoding = options.encoding;
|
||||
}
|
||||
}
|
||||
const map = unstable_getCacheForType(createReadlinkMap);
|
||||
let readlinkCache = map.get(path);
|
||||
if (!readlinkCache) {
|
||||
readlinkCache = [];
|
||||
map.set(path, readlinkCache);
|
||||
}
|
||||
let record;
|
||||
for (let i = 0; i < readlinkCache.length; i += 2) {
|
||||
const cachedEncoding: string = (readlinkCache[i]: any);
|
||||
if (encoding === cachedEncoding) {
|
||||
const cachedRecord: Record<void> = (readlinkCache[i + 1]: any);
|
||||
record = cachedRecord;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!record) {
|
||||
const thenable = fs.readlink(path, {encoding});
|
||||
record = createRecordFromThenable(thenable);
|
||||
// $FlowFixMe[incompatible-call] found when upgrading Flow
|
||||
readlinkCache.push(encoding, record);
|
||||
}
|
||||
// $FlowFixMe[incompatible-call] found when upgrading Flow
|
||||
const linkString = readRecord(record).value;
|
||||
return linkString;
|
||||
}
|
||||
|
||||
function createRealpathMap(): Map<string, Array<string | Record<mixed>>> {
|
||||
return new Map();
|
||||
}
|
||||
|
||||
export function realpath(
|
||||
path: string,
|
||||
options?: string | {encoding?: string},
|
||||
): mixed {
|
||||
checkPathInDev(path);
|
||||
let encoding = 'utf8';
|
||||
if (typeof options === 'string') {
|
||||
encoding = options;
|
||||
} else if (options != null) {
|
||||
if (options.encoding) {
|
||||
encoding = options.encoding;
|
||||
}
|
||||
}
|
||||
const map = unstable_getCacheForType(createRealpathMap);
|
||||
let realpathCache = map.get(path);
|
||||
if (!realpathCache) {
|
||||
realpathCache = [];
|
||||
map.set(path, realpathCache);
|
||||
}
|
||||
let record;
|
||||
for (let i = 0; i < realpathCache.length; i += 2) {
|
||||
const cachedEncoding: string = (realpathCache[i]: any);
|
||||
if (encoding === cachedEncoding) {
|
||||
const cachedRecord: Record<void> = (realpathCache[i + 1]: any);
|
||||
record = cachedRecord;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!record) {
|
||||
const thenable = fs.realpath(path, {encoding});
|
||||
record = createRecordFromThenable(thenable);
|
||||
// $FlowFixMe[incompatible-call] found when upgrading Flow
|
||||
realpathCache.push(encoding, record);
|
||||
}
|
||||
// $FlowFixMe[incompatible-call] found when upgrading Flow
|
||||
const resolvedPath = readRecord(record).value;
|
||||
return resolvedPath;
|
||||
}
|
||||
|
||||
function createStatMap(): Map<string, Array<boolean | Record<mixed>>> {
|
||||
return new Map();
|
||||
}
|
||||
|
||||
export function stat(path: string, options?: {bigint?: boolean}): mixed {
|
||||
checkPathInDev(path);
|
||||
let bigint = false;
|
||||
if (options && options.bigint) {
|
||||
bigint = true;
|
||||
}
|
||||
const map = unstable_getCacheForType(createStatMap);
|
||||
let statCache = map.get(path);
|
||||
if (!statCache) {
|
||||
statCache = [];
|
||||
map.set(path, statCache);
|
||||
}
|
||||
let record;
|
||||
for (let i = 0; i < statCache.length; i += 2) {
|
||||
const cachedBigint: boolean = (statCache[i]: any);
|
||||
if (bigint === cachedBigint) {
|
||||
const cachedRecord: Record<void> = (statCache[i + 1]: any);
|
||||
record = cachedRecord;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!record) {
|
||||
const thenable = fs.stat(path, {bigint});
|
||||
record = createRecordFromThenable(thenable);
|
||||
// $FlowFixMe[incompatible-call] found when upgrading Flow
|
||||
statCache.push(bigint, record);
|
||||
}
|
||||
// $FlowFixMe[incompatible-call] found when upgrading Flow
|
||||
const stats = readRecord(record).value;
|
||||
return stats;
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
# react-pg
|
||||
|
||||
This package is meant to be used alongside yet-to-be-released, experimental React features. It's unlikely to be useful in any other context.
|
||||
|
||||
**Do not use in a real application.** We're publishing this early for
|
||||
demonstration purposes.
|
||||
|
||||
**Use it at your own risk.**
|
||||
|
||||
# No, Really, It Is Unstable
|
||||
|
||||
The API ~~may~~ will change wildly between versions.
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
throw new Error(
|
||||
'This entry point is not yet supported in the browser environment',
|
||||
);
|
||||
13
packages/react-pg/index.js
vendored
13
packages/react-pg/index.js
vendored
|
|
@ -1,13 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
throw new Error(
|
||||
'React PG cannot be used outside a react-server environment. ' +
|
||||
'You must configure Node.js using the `--conditions react-server` flag.',
|
||||
);
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
export * from './src/ReactPostgres';
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
module.exports = require('./cjs/react-pg.browser.production.min.server.js');
|
||||
} else {
|
||||
module.exports = require('./cjs/react-pg.browser.development.server.js');
|
||||
}
|
||||
6
packages/react-pg/npm/index.js
vendored
6
packages/react-pg/npm/index.js
vendored
|
|
@ -1,6 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
throw new Error(
|
||||
'React PG cannot be used outside a react-server environment. ' +
|
||||
'You must configure Node.js using the `--conditions react-server` flag.'
|
||||
);
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
module.exports = require('./cjs/react-pg.node.production.min.server.js');
|
||||
} else {
|
||||
module.exports = require('./cjs/react-pg.node.development.server.js');
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
{
|
||||
"name": "react-pg",
|
||||
"description": "React bindings for PostgreSQL",
|
||||
"version": "0.0.0",
|
||||
"repository": {
|
||||
"type" : "git",
|
||||
"url" : "https://github.com/facebook/react.git",
|
||||
"directory": "packages/react-pg"
|
||||
},
|
||||
"files": [
|
||||
"LICENSE",
|
||||
"README.md",
|
||||
"index.js",
|
||||
"index.node.server.js",
|
||||
"index.browser.server.js",
|
||||
"cjs/"
|
||||
],
|
||||
"exports": {
|
||||
".": {
|
||||
"react-server": {
|
||||
"node": "./index.node.server.js",
|
||||
"browser": "./index.browser.server.js"
|
||||
},
|
||||
"default": "./index.js"
|
||||
},
|
||||
"./index.node.server": "./index.node.server.js",
|
||||
"./index.browser.server": "./index.browser.server.js",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^17.0.0",
|
||||
"pg": "*"
|
||||
}
|
||||
}
|
||||
124
packages/react-pg/src/ReactPostgres.js
vendored
124
packages/react-pg/src/ReactPostgres.js
vendored
|
|
@ -1,124 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and 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 {unstable_getCacheForType} from 'react';
|
||||
import {Pool as PostgresPool} from 'pg';
|
||||
import {prepareValue} from 'pg/lib/utils';
|
||||
|
||||
const Pending = 0;
|
||||
const Resolved = 1;
|
||||
const Rejected = 2;
|
||||
|
||||
type PendingRecord = {
|
||||
status: 0,
|
||||
value: Wakeable,
|
||||
};
|
||||
|
||||
type ResolvedRecord = {
|
||||
status: 1,
|
||||
value: mixed,
|
||||
};
|
||||
|
||||
type RejectedRecord = {
|
||||
status: 2,
|
||||
value: mixed,
|
||||
};
|
||||
|
||||
type Record = PendingRecord | ResolvedRecord | RejectedRecord;
|
||||
|
||||
function createRecordFromThenable(thenable): Record {
|
||||
const record: Record = {
|
||||
status: Pending,
|
||||
value: thenable,
|
||||
};
|
||||
thenable.then(
|
||||
value => {
|
||||
if (record.status === Pending) {
|
||||
const resolvedRecord = ((record: any): ResolvedRecord);
|
||||
resolvedRecord.status = Resolved;
|
||||
resolvedRecord.value = value;
|
||||
}
|
||||
},
|
||||
err => {
|
||||
if (record.status === Pending) {
|
||||
const rejectedRecord = ((record: any): RejectedRecord);
|
||||
rejectedRecord.status = Rejected;
|
||||
rejectedRecord.value = err;
|
||||
}
|
||||
},
|
||||
);
|
||||
return record;
|
||||
}
|
||||
|
||||
function readRecordValue(record: Record) {
|
||||
if (record.status === Resolved) {
|
||||
return record.value;
|
||||
} else {
|
||||
throw record.value;
|
||||
}
|
||||
}
|
||||
|
||||
export function Pool(options: mixed) {
|
||||
// $FlowFixMe[invalid-constructor] Flow no longer supports calling new on functions
|
||||
this.pool = new PostgresPool(options);
|
||||
// Unique function per instance because it's used for cache identity.
|
||||
this.createRecordMap = function() {
|
||||
return new Map();
|
||||
};
|
||||
}
|
||||
|
||||
type NestedMap = Map<any, Record | NestedMap>;
|
||||
|
||||
// $FlowFixMe[prop-missing] found when upgrading Flow
|
||||
Pool.prototype.query = function(query: string, values?: Array<mixed>) {
|
||||
const pool = this.pool;
|
||||
const outerMap = unstable_getCacheForType(this.createRecordMap);
|
||||
|
||||
let innerMap: NestedMap = outerMap;
|
||||
let key: mixed = query;
|
||||
if (values != null) {
|
||||
// If we have parameters, each becomes as a nesting layer for Maps.
|
||||
// We want to find (or create as needed) the innermost Map, and return that.
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
let nextMap = innerMap.get(key);
|
||||
if (nextMap === undefined) {
|
||||
nextMap = new Map();
|
||||
innerMap.set(key, nextMap);
|
||||
} else if (!(nextMap instanceof Map)) {
|
||||
throw new Error(
|
||||
'This query has received more parameters than the last time ' +
|
||||
'the same query was used. Always pass the exact number of ' +
|
||||
'parameters that the query needs.',
|
||||
);
|
||||
}
|
||||
innerMap = nextMap;
|
||||
// Postgres bindings convert everything to strings:
|
||||
// https://node-postgres.com/features/queries#parameterized-query
|
||||
// We reuse their algorithm instead of reimplementing.
|
||||
key = prepareValue(values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
let record = innerMap.get(key);
|
||||
if (!record) {
|
||||
const thenable = pool.query(query, values);
|
||||
record = createRecordFromThenable(thenable);
|
||||
innerMap.set(key, record);
|
||||
} else if (record instanceof Map) {
|
||||
throw new Error(
|
||||
'This query has received fewer parameters than the last time ' +
|
||||
'the same query was used. Always pass the exact number of ' +
|
||||
'parameters that the query needs.',
|
||||
);
|
||||
}
|
||||
const result = readRecordValue(record);
|
||||
return result;
|
||||
};
|
||||
|
|
@ -138,72 +138,6 @@ const bundles = [
|
|||
externals: ['react', 'ReactNativeInternalFeatureFlags'],
|
||||
},
|
||||
|
||||
/******* React Fetch Browser (experimental, new) *******/
|
||||
{
|
||||
bundleTypes: [NODE_DEV, NODE_PROD],
|
||||
moduleType: ISOMORPHIC,
|
||||
entry: 'react-fetch/index.browser',
|
||||
global: 'ReactFetch',
|
||||
minifyWithProdErrorCodes: false,
|
||||
wrapWithModuleBoundaries: false,
|
||||
externals: ['react'],
|
||||
},
|
||||
|
||||
/******* React Fetch Node (experimental, new) *******/
|
||||
{
|
||||
bundleTypes: [NODE_DEV, NODE_PROD],
|
||||
moduleType: ISOMORPHIC,
|
||||
entry: 'react-fetch/index.node',
|
||||
global: 'ReactFetch',
|
||||
minifyWithProdErrorCodes: false,
|
||||
wrapWithModuleBoundaries: false,
|
||||
externals: ['react', 'http', 'https'],
|
||||
},
|
||||
|
||||
/******* React FS Browser (experimental, new) *******/
|
||||
{
|
||||
bundleTypes: [NODE_DEV, NODE_PROD],
|
||||
moduleType: ISOMORPHIC,
|
||||
entry: 'react-fs/index.browser.server',
|
||||
global: 'ReactFilesystem',
|
||||
minifyWithProdErrorCodes: false,
|
||||
wrapWithModuleBoundaries: false,
|
||||
externals: [],
|
||||
},
|
||||
|
||||
/******* React FS Node (experimental, new) *******/
|
||||
{
|
||||
bundleTypes: [NODE_DEV, NODE_PROD],
|
||||
moduleType: ISOMORPHIC,
|
||||
entry: 'react-fs/index.node.server',
|
||||
global: 'ReactFilesystem',
|
||||
minifyWithProdErrorCodes: false,
|
||||
wrapWithModuleBoundaries: false,
|
||||
externals: ['react', 'fs/promises', 'path'],
|
||||
},
|
||||
|
||||
/******* React PG Browser (experimental, new) *******/
|
||||
{
|
||||
bundleTypes: [NODE_DEV, NODE_PROD],
|
||||
moduleType: ISOMORPHIC,
|
||||
entry: 'react-pg/index.browser.server',
|
||||
global: 'ReactPostgres',
|
||||
minifyWithProdErrorCodes: false,
|
||||
wrapWithModuleBoundaries: false,
|
||||
externals: [],
|
||||
},
|
||||
|
||||
/******* React PG Node (experimental, new) *******/
|
||||
{
|
||||
bundleTypes: [NODE_DEV, NODE_PROD],
|
||||
moduleType: ISOMORPHIC,
|
||||
entry: 'react-pg/index.node.server',
|
||||
global: 'ReactPostgres',
|
||||
minifyWithProdErrorCodes: false,
|
||||
wrapWithModuleBoundaries: false,
|
||||
externals: ['react', 'pg'],
|
||||
},
|
||||
|
||||
/******* React DOM *******/
|
||||
{
|
||||
bundleTypes: [
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ const importSideEffects = Object.freeze({
|
|||
react: HAS_NO_SIDE_EFFECTS_ON_IMPORT,
|
||||
'react-dom/server': HAS_NO_SIDE_EFFECTS_ON_IMPORT,
|
||||
'react/jsx-dev-runtime': HAS_NO_SIDE_EFFECTS_ON_IMPORT,
|
||||
'react-fetch/node': HAS_NO_SIDE_EFFECTS_ON_IMPORT,
|
||||
'react-dom': HAS_NO_SIDE_EFFECTS_ON_IMPORT,
|
||||
url: HAS_NO_SIDE_EFFECTS_ON_IMPORT,
|
||||
ReactNativeInternalFeatureFlags: HAS_NO_SIDE_EFFECTS_ON_IMPORT,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user