From 5bbcffa42cb4e3102e1305b16c218d00cf2cb8e1 Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Tue, 9 Jun 2020 21:36:00 -0700 Subject: [PATCH 1/3] Add Firestore ReactNative build This builds work out of the box in RN since it relies on js-base64 for base64 encoding. --- packages/firebase/src/index.rn.ts | 3 ++ packages/firestore/index.node.memory.ts | 2 +- packages/firestore/index.rn.memory.ts | 42 +++++++++++++++++++ packages/firestore/index.rn.ts | 41 ++++++++++++++++++ packages/firestore/memory/package.json | 1 + packages/firestore/package.json | 2 + packages/firestore/rollup.config.es2017.js | 27 +++++++++++- .../src/platform_browser/browser_platform.ts | 11 ++--- packages/firestore/src/platform_rn/rn_init.ts | 28 +++++++++++++ .../firestore/src/platform_rn/rn_platform.ts | 32 ++++++++++++++ yarn.lock | 10 +++++ 11 files changed, 190 insertions(+), 9 deletions(-) create mode 100644 packages/firestore/index.rn.memory.ts create mode 100644 packages/firestore/index.rn.ts create mode 100644 packages/firestore/src/platform_rn/rn_init.ts create mode 100644 packages/firestore/src/platform_rn/rn_platform.ts diff --git a/packages/firebase/src/index.rn.ts b/packages/firebase/src/index.rn.ts index b1e4c6dc083..c6e3c6f11bc 100644 --- a/packages/firebase/src/index.rn.ts +++ b/packages/firebase/src/index.rn.ts @@ -20,7 +20,10 @@ import { name, version } from '../package.json'; import '../auth'; import '../database'; +// TODO(b/158625454): Storage doesn't actually work by default in RN (it uses +// `atob`). We should provide a RN build that works out of the box. import '../storage'; +import '../firestore'; firebase.registerVersion(name, version, 'rn'); diff --git a/packages/firestore/index.node.memory.ts b/packages/firestore/index.node.memory.ts index d491d2a8dc1..1cfd55f8977 100644 --- a/packages/firestore/index.node.memory.ts +++ b/packages/firestore/index.node.memory.ts @@ -35,7 +35,7 @@ export function registerFirestore(instance: FirebaseNamespace): void { instance, (app, auth) => new Firestore(app, auth, new MemoryComponentProvider()) ); - instance.registerVersion(name, version); + instance.registerVersion(name, version, 'node'); } registerFirestore(firebase); diff --git a/packages/firestore/index.rn.memory.ts b/packages/firestore/index.rn.memory.ts new file mode 100644 index 00000000000..803e7be3c73 --- /dev/null +++ b/packages/firestore/index.rn.memory.ts @@ -0,0 +1,42 @@ +/** + * @license + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import firebase from '@firebase/app'; +import { FirebaseNamespace } from '@firebase/app-types'; + +import { Firestore } from './src/api/database'; +import { MemoryComponentProvider } from './src/core/component_provider'; +import { configureForFirebase } from './src/platform/config'; + +import './register-module'; +import './src/platform_rn/rn_init'; + +import { name, version } from './package.json'; + +/** + * Registers the memory-only Firestore build for ReactNative with the components + * framework. + */ +export function registerFirestore(instance: FirebaseNamespace): void { + configureForFirebase( + instance, + (app, auth) => new Firestore(app, auth, new MemoryComponentProvider()) + ); + instance.registerVersion(name, version, 'rn'); +} + +registerFirestore(firebase); diff --git a/packages/firestore/index.rn.ts b/packages/firestore/index.rn.ts new file mode 100644 index 00000000000..3130be10374 --- /dev/null +++ b/packages/firestore/index.rn.ts @@ -0,0 +1,41 @@ +/** + * @license + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import firebase from '@firebase/app'; +import { FirebaseNamespace } from '@firebase/app-types'; + +import { Firestore } from './src/api/database'; +import { IndexedDbComponentProvider } from './src/core/component_provider'; +import { configureForFirebase } from './src/platform/config'; + +import './register-module'; +import './src/platform_rn/rn_init'; + +import { name, version } from './package.json'; + +/** + * Registers the main Firestore ReactNative build with the components framework. + * Persistence can be enabled via `firebase.firestore().enablePersistence()`. + */ +export function registerFirestore(instance: FirebaseNamespace): void { + configureForFirebase( + instance, + (app, auth) => new Firestore(app, auth, new IndexedDbComponentProvider()) + ); + instance.registerVersion(name, version, 'rn'); +} + +registerFirestore(firebase); diff --git a/packages/firestore/memory/package.json b/packages/firestore/memory/package.json index ac0df500a26..04a27eb839a 100644 --- a/packages/firestore/memory/package.json +++ b/packages/firestore/memory/package.json @@ -3,6 +3,7 @@ "description": "A memory-only build of the Cloud Firestore JS SDK.", "main": "../dist/index.memory.node.cjs.js", "main-esm2017": "../dist/index.memory.node.esm2017.js", + "react-native": "../dist/index.memory.rn.esm2017.js", "browser": "../dist/index.memory.cjs.js", "module": "../dist/index.memory.esm.js", "esm2017": "../dist/index.memory.esm2017.js", diff --git a/packages/firestore/package.json b/packages/firestore/package.json index ba3e332455b..80391aa8aab 100644 --- a/packages/firestore/package.json +++ b/packages/firestore/package.json @@ -41,6 +41,7 @@ }, "main": "dist/index.node.cjs.js", "main-esm2017": "dist/index.node.esm2017.js", + "react-native": "dist/index.rn.esm2017.js", "browser": "dist/index.cjs.js", "module": "dist/index.esm.js", "esm2017": "dist/index.esm2017.js", @@ -58,6 +59,7 @@ "@firebase/webchannel-wrapper": "0.2.41", "@grpc/grpc-js": "0.8.1", "@grpc/proto-loader": "^0.5.0", + "js-base64": "2.5.2", "tslib": "1.11.1" }, "peerDependencies": { diff --git a/packages/firestore/rollup.config.es2017.js b/packages/firestore/rollup.config.es2017.js index fcabdabe0b1..9ef6e4fd326 100644 --- a/packages/firestore/rollup.config.es2017.js +++ b/packages/firestore/rollup.config.es2017.js @@ -100,6 +100,31 @@ const browserBuilds = [ } ]; +const reactNativeBuilds = [ + // Persistence build + { + input: 'index.rn.ts', + output: { + file: pkg['react-native'], + format: 'es', + sourcemap: true + }, + plugins: browserBuildPlugins, + external: resolveBrowserExterns + }, + // Memory-only build + { + input: 'index.rn.memory.ts', + output: { + file: path.resolve('./memory', memoryPkg['react-native']), + format: 'es', + sourcemap: true + }, + plugins: browserBuildPlugins, + external: resolveBrowserExterns + } +]; + // MARK: Node builds const nodeBuildPlugins = [ @@ -145,4 +170,4 @@ const nodeBuilds = [ } ]; -export default [...browserBuilds, ...nodeBuilds]; +export default [...browserBuilds, ...reactNativeBuilds, ...nodeBuilds]; diff --git a/packages/firestore/src/platform_browser/browser_platform.ts b/packages/firestore/src/platform_browser/browser_platform.ts index 38133db4df5..70a2dc9655c 100644 --- a/packages/firestore/src/platform_browser/browser_platform.ts +++ b/packages/firestore/src/platform_browser/browser_platform.ts @@ -19,7 +19,7 @@ import { DatabaseId, DatabaseInfo } from '../core/database_info'; import { Platform } from '../platform/platform'; import { Connection } from '../remote/connection'; import { JsonProtoSerializer } from '../remote/serializer'; -import { ConnectivityMonitor } from './../remote/connectivity_monitor'; +import { ConnectivityMonitor } from '../remote/connectivity_monitor'; import { NoopConnectivityMonitor } from '../remote/connectivity_monitor_noop'; import { BrowserConnectivityMonitor } from './browser_connectivity_monitor'; @@ -27,13 +27,10 @@ import { WebChannelConnection } from './webchannel_connection'; import { debugAssert } from '../util/assert'; // Implements the Platform API for browsers and some browser-like environments -// (including ReactNative). +// (including ReactNative, which has its own ReactNativePlatform that extends +// from this class). export class BrowserPlatform implements Platform { - readonly base64Available: boolean; - - constructor() { - this.base64Available = typeof atob !== 'undefined'; - } + readonly base64Available = typeof atob !== 'undefined'; get document(): Document | null { // `document` is not always available, e.g. in ReactNative and WebWorkers. diff --git a/packages/firestore/src/platform_rn/rn_init.ts b/packages/firestore/src/platform_rn/rn_init.ts new file mode 100644 index 00000000000..442a86d079c --- /dev/null +++ b/packages/firestore/src/platform_rn/rn_init.ts @@ -0,0 +1,28 @@ +/** + * @license + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { PlatformSupport } from '../platform/platform'; +import { ReactNativePlatform } from './rn_platform'; + +/** + * This code needs to run before Firestore is used. This can be achieved in + * several ways: + * 1) Through the JSCompiler compiling this code and then (automatically) + * executing it before exporting the Firestore symbols. + * 2) Through importing this module first in a Firestore main module + */ +PlatformSupport.setPlatform(new ReactNativePlatform()); diff --git a/packages/firestore/src/platform_rn/rn_platform.ts b/packages/firestore/src/platform_rn/rn_platform.ts new file mode 100644 index 00000000000..9667ace2e42 --- /dev/null +++ b/packages/firestore/src/platform_rn/rn_platform.ts @@ -0,0 +1,32 @@ +/** + * @license + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BrowserPlatform } from '../platform_browser/browser_platform'; +import { Base64 } from 'js-base64'; + +// Implements the Platform API for ReactNative. +export class ReactNativePlatform extends BrowserPlatform { + readonly base64Available = true; + + atob(encoded: string): string { + return Base64.decode(encoded); + } + + btoa(raw: string): string { + return Base64.encode(raw); + } +} diff --git a/yarn.lock b/yarn.lock index 1591ae97d02..6b576270d07 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2130,6 +2130,11 @@ "@types/minimatch" "*" "@types/node" "*" +"@types/js-base64@2.3.1": + version "2.3.1" + resolved "https://registry.npmjs.org/@types/js-base64/-/js-base64-2.3.1.tgz#c39f14f129408a3d96a1105a650d8b2b6eeb4168" + integrity sha512-4RKbhIDGC87s4EBy2Cp2/5S2O6kmCRcZnD5KRCq1q9z2GhBte1+BdsfVKCpG8yKpDGNyEE2G6IqFIh6W2YwWPA== + "@types/json-schema@^7.0.3": version "7.0.4" resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339" @@ -8730,6 +8735,11 @@ jquery@^3.4.1: resolved "https://registry.npmjs.org/jquery/-/jquery-3.4.1.tgz#714f1f8d9dde4bdfa55764ba37ef214630d80ef2" integrity sha512-36+AdBzCL+y6qjw5Tx7HgzeGCzC81MDDgaUP8ld2zhx58HdqXGoBd+tHdrBMiyjGQs0Hxs/MLZTu/eHNJJuWPw== +js-base64@2.5.2: + version "2.5.2" + resolved "https://registry.npmjs.org/js-base64/-/js-base64-2.5.2.tgz#313b6274dda718f714d00b3330bbae6e38e90209" + integrity sha512-Vg8czh0Q7sFBSUMWWArX/miJeBWYBPpdU/3M/DKSaekLMqrqVPaedp+5mZhie/r0lgrcaYBfwXatEew6gwgiQQ== + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" From d51d6bf9d57bb4ee5f0e29c3d2234e7d3533f64e Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Tue, 9 Jun 2020 21:41:25 -0700 Subject: [PATCH 2/3] Add @types/js-base64 --- packages/firestore/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/firestore/package.json b/packages/firestore/package.json index 80391aa8aab..1d3e5aae555 100644 --- a/packages/firestore/package.json +++ b/packages/firestore/package.json @@ -70,6 +70,7 @@ "@firebase/app-exp": "0.x", "@firebase/app-types-exp": "0.x", "@types/json-stable-stringify": "1.0.32", + "@types/js-base64": "2.3.1", "json-stable-stringify": "1.0.1", "protobufjs": "6.9.0", "rollup": "2.7.6", From da7efb89d70b8d4dd2e93c5cb54a42105d2f74e0 Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Wed, 10 Jun 2020 12:43:36 -0700 Subject: [PATCH 3/3] Use base64 from firebase/util --- packages/firestore/package.json | 2 -- packages/firestore/src/platform_rn/rn_platform.ts | 10 +++++++--- yarn.lock | 10 ---------- 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/packages/firestore/package.json b/packages/firestore/package.json index 1d3e5aae555..7018bf88ba4 100644 --- a/packages/firestore/package.json +++ b/packages/firestore/package.json @@ -59,7 +59,6 @@ "@firebase/webchannel-wrapper": "0.2.41", "@grpc/grpc-js": "0.8.1", "@grpc/proto-loader": "^0.5.0", - "js-base64": "2.5.2", "tslib": "1.11.1" }, "peerDependencies": { @@ -70,7 +69,6 @@ "@firebase/app-exp": "0.x", "@firebase/app-types-exp": "0.x", "@types/json-stable-stringify": "1.0.32", - "@types/js-base64": "2.3.1", "json-stable-stringify": "1.0.1", "protobufjs": "6.9.0", "rollup": "2.7.6", diff --git a/packages/firestore/src/platform_rn/rn_platform.ts b/packages/firestore/src/platform_rn/rn_platform.ts index 9667ace2e42..2ce9d9bfbd9 100644 --- a/packages/firestore/src/platform_rn/rn_platform.ts +++ b/packages/firestore/src/platform_rn/rn_platform.ts @@ -16,17 +16,21 @@ */ import { BrowserPlatform } from '../platform_browser/browser_platform'; -import { Base64 } from 'js-base64'; +import { base64 } from '@firebase/util'; // Implements the Platform API for ReactNative. export class ReactNativePlatform extends BrowserPlatform { readonly base64Available = true; atob(encoded: string): string { - return Base64.decode(encoded); + // WebSafe uses a different URL-encoding safe alphabet that doesn't match + // the encoding used on the backend. + return base64.decodeString(encoded, /* webSafe =*/ false); } btoa(raw: string): string { - return Base64.encode(raw); + // WebSafe uses a different URL-encoding safe alphabet that doesn't match + // the encoding used on the backend. + return base64.encodeString(raw, /* webSafe =*/ false); } } diff --git a/yarn.lock b/yarn.lock index 6b576270d07..1591ae97d02 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2130,11 +2130,6 @@ "@types/minimatch" "*" "@types/node" "*" -"@types/js-base64@2.3.1": - version "2.3.1" - resolved "https://registry.npmjs.org/@types/js-base64/-/js-base64-2.3.1.tgz#c39f14f129408a3d96a1105a650d8b2b6eeb4168" - integrity sha512-4RKbhIDGC87s4EBy2Cp2/5S2O6kmCRcZnD5KRCq1q9z2GhBte1+BdsfVKCpG8yKpDGNyEE2G6IqFIh6W2YwWPA== - "@types/json-schema@^7.0.3": version "7.0.4" resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339" @@ -8735,11 +8730,6 @@ jquery@^3.4.1: resolved "https://registry.npmjs.org/jquery/-/jquery-3.4.1.tgz#714f1f8d9dde4bdfa55764ba37ef214630d80ef2" integrity sha512-36+AdBzCL+y6qjw5Tx7HgzeGCzC81MDDgaUP8ld2zhx58HdqXGoBd+tHdrBMiyjGQs0Hxs/MLZTu/eHNJJuWPw== -js-base64@2.5.2: - version "2.5.2" - resolved "https://registry.npmjs.org/js-base64/-/js-base64-2.5.2.tgz#313b6274dda718f714d00b3330bbae6e38e90209" - integrity sha512-Vg8czh0Q7sFBSUMWWArX/miJeBWYBPpdU/3M/DKSaekLMqrqVPaedp+5mZhie/r0lgrcaYBfwXatEew6gwgiQQ== - "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"