@samitouri / QOS-React-2 / commits / 60e4a76fa8

[Flight] Add rudimentary PG binding (#20372)

* [Flight] Add rudimentary PG binding * Use nested Maps for parameters * Inline and fix Flow

Dan Abramov committed Dec 4, 2020 at 17:27 UTC 60e4a76fa872e429975e336827f8c0a51394110f
11 files changed +231
packages/react-pg/README.md new
+12
@@ -0,0 +1,12 @@
1 +# react-pg
2 +
3 +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.
4 +
5 +**Do not use in a real application.** We're publishing this early for
6 +demonstration purposes.
7 +
8 +**Use it at your own risk.**
9 +
10 +# No, Really, It Is Unstable
11 +
12 +The API ~~may~~ will change wildly between versions.
packages/react-pg/index.browser.js new
+12
@@ -0,0 +1,12 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @flow
8 + */
9 +
10 +throw new Error(
11 + 'This entry point is not yet supported in the browser environment',
12 +);
packages/react-pg/index.js new
+12
@@ -0,0 +1,12 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @flow
8 + */
9 +
10 +'use strict';
11 +
12 +export * from './index.node';
packages/react-pg/index.node.js new
+12
@@ -0,0 +1,12 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @flow
8 + */
9 +
10 +'use strict';
11 +
12 +export * from './src/ReactPostgres';
packages/react-pg/npm/index.browser.js new
+7
@@ -0,0 +1,7 @@
1 +'use strict';
2 +
3 +if (process.env.NODE_ENV === 'production') {
4 + module.exports = require('./cjs/react-pg.browser.production.min.js');
5 +} else {
6 + module.exports = require('./cjs/react-pg.browser.development.js');
7 +}
packages/react-pg/npm/index.js new
+3
@@ -0,0 +1,3 @@
1 +'use strict';
2 +
3 +module.exports = require('./index.node');
packages/react-pg/npm/index.node.js new
+7
@@ -0,0 +1,7 @@
1 +'use strict';
2 +
3 +if (process.env.NODE_ENV === 'production') {
4 + module.exports = require('./cjs/react-pg.node.production.min.js');
5 +} else {
6 + module.exports = require('./cjs/react-pg.node.development.js');
7 +}
packages/react-pg/package.json new
+27
@@ -0,0 +1,27 @@
1 +{
2 + "private": true,
3 + "name": "react-pg",
4 + "description": "React bindings for PostgreSQL",
5 + "version": "0.0.0",
6 + "repository": {
7 + "type" : "git",
8 + "url" : "https://github.com/facebook/react.git",
9 + "directory": "packages/react-pg"
10 + },
11 + "files": [
12 + "LICENSE",
13 + "README.md",
14 + "build-info.json",
15 + "index.js",
16 + "index.node.js",
17 + "index.browser.js",
18 + "cjs/"
19 + ],
20 + "peerDependencies": {
21 + "react": "^17.0.0",
22 + "pg": "*"
23 + },
24 + "browser": {
25 + "./index.js": "./index.browser.js"
26 + }
27 +}
packages/react-pg/src/ReactPostgres.js new
+107
@@ -0,0 +1,107 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @flow
8 + */
9 +
10 +import type {Wakeable} from 'shared/ReactTypes';
11 +
12 +import {unstable_getCacheForType} from 'react';
13 +import {Pool as PostgresPool} from 'pg';
14 +import {prepareValue} from 'pg/lib/utils';
15 +
16 +const Pending = 0;
17 +const Resolved = 1;
18 +const Rejected = 2;
19 +
20 +type PendingResult = {|
21 + status: 0,
22 + value: Wakeable,
23 +|};
24 +
25 +type ResolvedResult = {|
26 + status: 1,
27 + value: mixed,
28 +|};
29 +
30 +type RejectedResult = {|
31 + status: 2,
32 + value: mixed,
33 +|};
34 +
35 +type Result = PendingResult | ResolvedResult | RejectedResult;
36 +
37 +function toResult(thenable): Result {
38 + const result: Result = {
39 + status: Pending,
40 + value: thenable,
41 + };
42 + thenable.then(
43 + value => {
44 + if (result.status === Pending) {
45 + const resolvedResult = ((result: any): ResolvedResult);
46 + resolvedResult.status = Resolved;
47 + resolvedResult.value = value;
48 + }
49 + },
50 + err => {
51 + if (result.status === Pending) {
52 + const rejectedResult = ((result: any): RejectedResult);
53 + rejectedResult.status = Rejected;
54 + rejectedResult.value = err;
55 + }
56 + },
57 + );
58 + return result;
59 +}
60 +
61 +function readResult(result: Result) {
62 + if (result.status === Resolved) {
63 + return result.value;
64 + } else {
65 + throw result.value;
66 + }
67 +}
68 +
69 +export function Pool(options: mixed) {
70 + this.pool = new PostgresPool(options);
71 + // Unique function per instance because it's used for cache identity.
72 + this.createResultMap = function() {
73 + return new Map();
74 + };
75 +}
76 +
77 +Pool.prototype.query = function(query: string, values?: Array<mixed>) {
78 + const pool = this.pool;
79 + const outerMap = unstable_getCacheForType(this.createResultMap);
80 +
81 + let innerMap: Map<any, any> = outerMap;
82 + let key = query;
83 + if (values != null) {
84 + // If we have parameters, each becomes as a nesting layer for Maps.
85 + // We want to find (or create as needed) the innermost Map, and return that.
86 + for (let i = 0; i < values.length; i++) {
87 + let nextMap = innerMap.get(key);
88 + if (nextMap === undefined) {
89 + nextMap = new Map();
90 + innerMap.set(key, nextMap);
91 + }
92 + innerMap = nextMap;
93 + // Postgres bindings convert everything to strings:
94 + // https://node-postgres.com/features/queries#parameterized-query
95 + // We reuse their algorithm instead of reimplementing.
96 + key = prepareValue(values[i]);
97 + }
98 + }
99 +
100 + let entry: Result | void = innerMap.get(key);
101 + if (!entry) {
102 + const thenable = pool.query(query, values);
103 + entry = toResult(thenable);
104 + innerMap.set(key, entry);
105 + }
106 + return readResult(entry);
107 +};
scripts/flow/environment.js
+14
@@ -69,3 +69,17 @@ declare module 'EventListener' {
69
70 declare function __webpack_chunk_load__(id: string): Promise<mixed>;
71 declare function __webpack_require__(id: string): any;
72 +
73 +declare module 'pg' {
74 + declare var Pool: (
75 + options: mixed,
76 + ) => {
77 + query: (query: string, values?: Array<mixed>) => void,
78 + };
79 +}
80 +
81 +declare module 'pg/lib/utils' {
82 + declare module.exports: {
83 + prepareValue(val: any): mixed,
84 + };
85 +}
scripts/rollup/bundles.js
+18
@@ -153,6 +153,24 @@ const bundles = [
153 externals: ['react', 'http', 'https'],
154 },
155
156 + /******* React PG Browser (experimental, new) *******/
157 + {
158 + bundleTypes: [NODE_DEV, NODE_PROD],
159 + moduleType: ISOMORPHIC,
160 + entry: 'react-pg/index.browser',
161 + global: 'ReactPostgres',
162 + externals: [],
163 + },
164 +
165 + /******* React PG Node (experimental, new) *******/
166 + {
167 + bundleTypes: [NODE_DEV, NODE_PROD],
168 + moduleType: ISOMORPHIC,
169 + entry: 'react-pg/index.node',
170 + global: 'ReactPostgres',
171 + externals: ['react', 'pg'],
172 + },
173 +
174 /******* React DOM *******/
175 {
176 bundleTypes: [