From a7671b0d1b123350b9a0849d1340c53c22adaa6e Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 3 Dec 2020 22:31:13 +0000 Subject: [PATCH] [Flight] Add rudimentary PG binding --- packages/react-pg/README.md | 12 ++++ packages/react-pg/index.browser.js | 12 ++++ packages/react-pg/index.js | 12 ++++ packages/react-pg/index.node.js | 12 ++++ packages/react-pg/npm/index.browser.js | 7 ++ packages/react-pg/npm/index.js | 3 + packages/react-pg/npm/index.node.js | 7 ++ packages/react-pg/package.json | 27 ++++++++ packages/react-pg/src/ReactPostgres.js | 88 ++++++++++++++++++++++++++ scripts/flow/environment.js | 6 ++ scripts/rollup/bundles.js | 20 ++++++ 11 files changed, 206 insertions(+) create mode 100644 packages/react-pg/README.md create mode 100644 packages/react-pg/index.browser.js create mode 100644 packages/react-pg/index.js create mode 100644 packages/react-pg/index.node.js create mode 100644 packages/react-pg/npm/index.browser.js create mode 100644 packages/react-pg/npm/index.js create mode 100644 packages/react-pg/npm/index.node.js create mode 100644 packages/react-pg/package.json create mode 100644 packages/react-pg/src/ReactPostgres.js diff --git a/packages/react-pg/README.md b/packages/react-pg/README.md new file mode 100644 index 0000000000000..cbcc8fb09a7fe --- /dev/null +++ b/packages/react-pg/README.md @@ -0,0 +1,12 @@ +# 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.js b/packages/react-pg/index.browser.js new file mode 100644 index 0000000000000..444c63ec765ff --- /dev/null +++ b/packages/react-pg/index.browser.js @@ -0,0 +1,12 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +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 new file mode 100644 index 0000000000000..ceb2071c4f055 --- /dev/null +++ b/packages/react-pg/index.js @@ -0,0 +1,12 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +'use strict'; + +export * from './index.node'; diff --git a/packages/react-pg/index.node.js b/packages/react-pg/index.node.js new file mode 100644 index 0000000000000..71ee93be80aa8 --- /dev/null +++ b/packages/react-pg/index.node.js @@ -0,0 +1,12 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +'use strict'; + +export * from './src/ReactPostgres'; diff --git a/packages/react-pg/npm/index.browser.js b/packages/react-pg/npm/index.browser.js new file mode 100644 index 0000000000000..6e11ea9c992e1 --- /dev/null +++ b/packages/react-pg/npm/index.browser.js @@ -0,0 +1,7 @@ +'use strict'; + +if (process.env.NODE_ENV === 'production') { + module.exports = require('./cjs/react-pg.browser.production.min.js'); +} else { + module.exports = require('./cjs/react-pg.browser.development.js'); +} diff --git a/packages/react-pg/npm/index.js b/packages/react-pg/npm/index.js new file mode 100644 index 0000000000000..ee510df2ad686 --- /dev/null +++ b/packages/react-pg/npm/index.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports = require('./index.node'); diff --git a/packages/react-pg/npm/index.node.js b/packages/react-pg/npm/index.node.js new file mode 100644 index 0000000000000..a351531e3c902 --- /dev/null +++ b/packages/react-pg/npm/index.node.js @@ -0,0 +1,7 @@ +'use strict'; + +if (process.env.NODE_ENV === 'production') { + module.exports = require('./cjs/react-pg.node.production.min.js'); +} else { + module.exports = require('./cjs/react-pg.node.development.js'); +} diff --git a/packages/react-pg/package.json b/packages/react-pg/package.json new file mode 100644 index 0000000000000..9f8575414eb45 --- /dev/null +++ b/packages/react-pg/package.json @@ -0,0 +1,27 @@ +{ + "private": true, + "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", + "build-info.json", + "index.js", + "index.node.js", + "index.browser.js", + "cjs/" + ], + "peerDependencies": { + "react": "^17.0.0", + "pg": "*" + }, + "browser": { + "./index.js": "./index.browser.js" + } +} diff --git a/packages/react-pg/src/ReactPostgres.js b/packages/react-pg/src/ReactPostgres.js new file mode 100644 index 0000000000000..5e02dc995bea7 --- /dev/null +++ b/packages/react-pg/src/ReactPostgres.js @@ -0,0 +1,88 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +import type {Wakeable} from 'shared/ReactTypes'; + +import {unstable_getCacheForType} from 'react'; +import {Pool as PostgresPool} from 'pg'; + +const Pending = 0; +const Resolved = 1; +const Rejected = 2; + +type PendingResult = {| + status: 0, + value: Wakeable, +|}; + +type ResolvedResult = {| + status: 1, + value: mixed, +|}; + +type RejectedResult = {| + status: 2, + value: mixed, +|}; + +type Result = PendingResult | ResolvedResult | RejectedResult; + +function toResult(thenable): Result { + const result: Result = { + status: Pending, + value: thenable, + }; + thenable.then( + value => { + if (result.status === Pending) { + const resolvedResult = ((result: any): ResolvedResult); + resolvedResult.status = Resolved; + resolvedResult.value = value; + } + }, + err => { + if (result.status === Pending) { + const rejectedResult = ((result: any): RejectedResult); + rejectedResult.status = Rejected; + rejectedResult.value = err; + } + }, + ); + return result; +} + +function readResult(result: Result) { + if (result.status === Resolved) { + return result.value; + } else { + throw result.value; + } +} + +export function Pool(options: mixed) { + this.pool = new PostgresPool(options); + // Unique function per instance because it's used for cache identity. + this.createResultMap = function() { + return new Map(); + }; +} + +Pool.prototype.query = function(query: string, values?: Array) { + const pool = this.pool; + const map = unstable_getCacheForType(this.createResultMap); + // TODO: Is this sufficient? What about more complex types? + const key = JSON.stringify({query, values}); + let entry = map.get(key); + if (!entry) { + const thenable = pool.query(query, values); + entry = toResult(thenable); + map.set(key, entry); + } + return readResult(entry); +} diff --git a/scripts/flow/environment.js b/scripts/flow/environment.js index 3efe41ff344d2..968a42cb89205 100644 --- a/scripts/flow/environment.js +++ b/scripts/flow/environment.js @@ -69,3 +69,9 @@ declare module 'EventListener' { declare function __webpack_chunk_load__(id: string): Promise; declare function __webpack_require__(id: string): any; + +declare module 'pg' { + declare var Pool: (options: mixed) => { + query: (query: string, values?: Array) => void + }; +} diff --git a/scripts/rollup/bundles.js b/scripts/rollup/bundles.js index 37fdafa3408a1..8021e89604403 100644 --- a/scripts/rollup/bundles.js +++ b/scripts/rollup/bundles.js @@ -153,6 +153,26 @@ const bundles = [ externals: ['react', 'http', 'https'], }, + + /******* React PG Browser (experimental, new) *******/ + { + bundleTypes: [NODE_DEV, NODE_PROD], + moduleType: ISOMORPHIC, + entry: 'react-pg/index.browser', + global: 'ReactPostgres', + externals: [], + }, + + + /******* React PG Node (experimental, new) *******/ + { + bundleTypes: [NODE_DEV, NODE_PROD], + moduleType: ISOMORPHIC, + entry: 'react-pg/index.node', + global: 'ReactPostgres', + externals: ['react', 'pg'], + }, + /******* React DOM *******/ { bundleTypes: [