diff --git a/.eslintrc.js b/.eslintrc.js index c0b751ad85..c535007467 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -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', diff --git a/ReactVersions.js b/ReactVersions.js index 26701175ec..789068abeb 100644 --- a/ReactVersions.js +++ b/ReactVersions.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, diff --git a/packages/react-fetch/README.md b/packages/react-fetch/README.md deleted file mode 100644 index 95bc5804bf..0000000000 --- a/packages/react-fetch/README.md +++ /dev/null @@ -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. diff --git a/packages/react-fetch/index.browser.js b/packages/react-fetch/index.browser.js deleted file mode 100644 index e6be1f768d..0000000000 --- a/packages/react-fetch/index.browser.js +++ /dev/null @@ -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'; diff --git a/packages/react-fetch/index.js b/packages/react-fetch/index.js deleted file mode 100644 index b4ba91a642..0000000000 --- a/packages/react-fetch/index.js +++ /dev/null @@ -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'; diff --git a/packages/react-fetch/index.node.js b/packages/react-fetch/index.node.js deleted file mode 100644 index 2ed1e4210f..0000000000 --- a/packages/react-fetch/index.node.js +++ /dev/null @@ -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'; diff --git a/packages/react-fetch/npm/index.browser.js b/packages/react-fetch/npm/index.browser.js deleted file mode 100644 index 6ecf769fed..0000000000 --- a/packages/react-fetch/npm/index.browser.js +++ /dev/null @@ -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'); -} diff --git a/packages/react-fetch/npm/index.js b/packages/react-fetch/npm/index.js deleted file mode 100644 index ee510df2ad..0000000000 --- a/packages/react-fetch/npm/index.js +++ /dev/null @@ -1,3 +0,0 @@ -'use strict'; - -module.exports = require('./index.node'); diff --git a/packages/react-fetch/npm/index.node.js b/packages/react-fetch/npm/index.node.js deleted file mode 100644 index bc76412003..0000000000 --- a/packages/react-fetch/npm/index.node.js +++ /dev/null @@ -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'); -} diff --git a/packages/react-fetch/package.json b/packages/react-fetch/package.json deleted file mode 100644 index 7acbaab4b2..0000000000 --- a/packages/react-fetch/package.json +++ /dev/null @@ -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" - } -} diff --git a/packages/react-fetch/src/ReactFetchBrowser.js b/packages/react-fetch/src/ReactFetchBrowser.js deleted file mode 100644 index fde02b8dec..0000000000 --- a/packages/react-fetch/src/ReactFetchBrowser.js +++ /dev/null @@ -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 { - return unstable_getCacheForType(createRecordMap); -} - -function createRecordMap(): Map { - 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)); - } -} diff --git a/packages/react-fetch/src/ReactFetchNode.js b/packages/react-fetch/src/ReactFetchNode.js deleted file mode 100644 index d0bd49e5bb..0000000000 --- a/packages/react-fetch/src/ReactFetchNode.js +++ /dev/null @@ -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 = { - status: 1, - value: V, -}; - -type RejectedRecord = { - status: 2, - value: mixed, -}; - -type Record = PendingRecord | ResolvedRecord | RejectedRecord; - -function getRecordMap(): Map> { - return unstable_getCacheForType(createRecordMap); -} - -function createRecordMap(): Map> { - return new Map(); -} - -function readRecordValue(record: Record): 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); - 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 { - 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 = (record = { - status: Pending, - value: wakeable, - }); - nodeFetch( - url, - options, - response => { - if (newRecord.status === Pending) { - const resolvedRecord = ((newRecord: any): ResolvedRecord); - 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; -} diff --git a/packages/react-fetch/src/__tests__/ReactFetchBrowser-test.js b/packages/react-fetch/src/__tests__/ReactFetchBrowser-test.js deleted file mode 100644 index 3e06bcfa5f..0000000000 --- a/packages/react-fetch/src/__tests__/ReactFetchBrowser-test.js +++ /dev/null @@ -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); - }); -}); diff --git a/packages/react-fetch/src/__tests__/ReactFetchNode-test.js b/packages/react-fetch/src/__tests__/ReactFetchNode-test.js deleted file mode 100644 index c4e41a0cd9..0000000000 --- a/packages/react-fetch/src/__tests__/ReactFetchNode-test.js +++ /dev/null @@ -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'); - } - }); -}); diff --git a/packages/react-fs/README.md b/packages/react-fs/README.md deleted file mode 100644 index cca1192f52..0000000000 --- a/packages/react-fs/README.md +++ /dev/null @@ -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. diff --git a/packages/react-fs/index.browser.server.js b/packages/react-fs/index.browser.server.js deleted file mode 100644 index 1ac305fdf1..0000000000 --- a/packages/react-fs/index.browser.server.js +++ /dev/null @@ -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', -); diff --git a/packages/react-fs/index.js b/packages/react-fs/index.js deleted file mode 100644 index 2800148fd1..0000000000 --- a/packages/react-fs/index.js +++ /dev/null @@ -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.', -); diff --git a/packages/react-fs/index.node.server.js b/packages/react-fs/index.node.server.js deleted file mode 100644 index dab98bd88c..0000000000 --- a/packages/react-fs/index.node.server.js +++ /dev/null @@ -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'; diff --git a/packages/react-fs/npm/index.browser.server.js b/packages/react-fs/npm/index.browser.server.js deleted file mode 100644 index 1bb15134d6..0000000000 --- a/packages/react-fs/npm/index.browser.server.js +++ /dev/null @@ -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'); -} diff --git a/packages/react-fs/npm/index.js b/packages/react-fs/npm/index.js deleted file mode 100644 index 39ea7955e3..0000000000 --- a/packages/react-fs/npm/index.js +++ /dev/null @@ -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.' -); diff --git a/packages/react-fs/npm/index.node.server.js b/packages/react-fs/npm/index.node.server.js deleted file mode 100644 index 58f2b42283..0000000000 --- a/packages/react-fs/npm/index.node.server.js +++ /dev/null @@ -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'); -} diff --git a/packages/react-fs/package.json b/packages/react-fs/package.json deleted file mode 100644 index 00c1c589b2..0000000000 --- a/packages/react-fs/package.json +++ /dev/null @@ -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" - } -} diff --git a/packages/react-fs/src/ReactFilesystem.js b/packages/react-fs/src/ReactFilesystem.js deleted file mode 100644 index 996874896e..0000000000 --- a/packages/react-fs/src/ReactFilesystem.js +++ /dev/null @@ -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 = { - status: 1, - value: T, - cache: null | Array, -}; - -type RejectedRecord = { - status: 2, - value: mixed, - cache: null, -}; - -type Record = PendingRecord | ResolvedRecord | RejectedRecord; - -function createRecordFromThenable(thenable: Thenable): Record { - const record: Record = { - status: Pending, - value: thenable, - cache: null, - }; - 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 readRecord(record: Record): ResolvedRecord { - 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>> { - 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 = (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>> { - 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 = (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>, -> { - 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 = (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> { - 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>> { - 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 = (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>> { - 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 = (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>> { - 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 = (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; -} diff --git a/packages/react-pg/README.md b/packages/react-pg/README.md deleted file mode 100644 index cbcc8fb09a..0000000000 --- a/packages/react-pg/README.md +++ /dev/null @@ -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. diff --git a/packages/react-pg/index.browser.server.js b/packages/react-pg/index.browser.server.js deleted file mode 100644 index 1ac305fdf1..0000000000 --- a/packages/react-pg/index.browser.server.js +++ /dev/null @@ -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', -); diff --git a/packages/react-pg/index.js b/packages/react-pg/index.js deleted file mode 100644 index 11a3f83336..0000000000 --- a/packages/react-pg/index.js +++ /dev/null @@ -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.', -); diff --git a/packages/react-pg/index.node.server.js b/packages/react-pg/index.node.server.js deleted file mode 100644 index 8ff3b401fc..0000000000 --- a/packages/react-pg/index.node.server.js +++ /dev/null @@ -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'; diff --git a/packages/react-pg/npm/index.browser.server.js b/packages/react-pg/npm/index.browser.server.js deleted file mode 100644 index 175cec16ae..0000000000 --- a/packages/react-pg/npm/index.browser.server.js +++ /dev/null @@ -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'); -} diff --git a/packages/react-pg/npm/index.js b/packages/react-pg/npm/index.js deleted file mode 100644 index 64e0b2b2b5..0000000000 --- a/packages/react-pg/npm/index.js +++ /dev/null @@ -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.' -); diff --git a/packages/react-pg/npm/index.node.server.js b/packages/react-pg/npm/index.node.server.js deleted file mode 100644 index c49f5fe645..0000000000 --- a/packages/react-pg/npm/index.node.server.js +++ /dev/null @@ -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'); -} diff --git a/packages/react-pg/package.json b/packages/react-pg/package.json deleted file mode 100644 index 25743847b1..0000000000 --- a/packages/react-pg/package.json +++ /dev/null @@ -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": "*" - } -} diff --git a/packages/react-pg/src/ReactPostgres.js b/packages/react-pg/src/ReactPostgres.js deleted file mode 100644 index dd4521ec0c..0000000000 --- a/packages/react-pg/src/ReactPostgres.js +++ /dev/null @@ -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; - -// $FlowFixMe[prop-missing] found when upgrading Flow -Pool.prototype.query = function(query: string, values?: Array) { - 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; -}; diff --git a/scripts/rollup/bundles.js b/scripts/rollup/bundles.js index 7325bb41b3..6563bb62b2 100644 --- a/scripts/rollup/bundles.js +++ b/scripts/rollup/bundles.js @@ -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: [ diff --git a/scripts/rollup/modules.js b/scripts/rollup/modules.js index 68fa90bc3b..3cce363f3d 100644 --- a/scripts/rollup/modules.js +++ b/scripts/rollup/modules.js @@ -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,