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:
Sebastian Markbåge 2022-10-27 17:52:53 -04:00 committed by GitHub
parent 28a574ea8f
commit cf3932be5c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 1 additions and 1441 deletions

View File

@ -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',

View File

@ -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,

View File

@ -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.

View File

@ -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';

View File

@ -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';

View File

@ -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';

View File

@ -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');
}

View File

@ -1,3 +0,0 @@
'use strict';
module.exports = require('./index.node');

View File

@ -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');
}

View File

@ -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"
}
}

View File

@ -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));
}
}

View File

@ -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;
}

View File

@ -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);
});
});

View File

@ -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');
}
});
});

View File

@ -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.

View File

@ -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',
);

View File

@ -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.',
);

View File

@ -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';

View File

@ -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');
}

View File

@ -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.'
);

View File

@ -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');
}

View File

@ -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"
}
}

View File

@ -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;
}

View File

@ -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.

View File

@ -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',
);

View File

@ -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.',
);

View File

@ -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';

View File

@ -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');
}

View File

@ -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.'
);

View File

@ -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');
}

View File

@ -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": "*"
}
}

View File

@ -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;
};

View File

@ -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: [

View File

@ -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,