From 163fc2ad7a082d959476cb2f688c63013d245631 Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Mon, 22 Jun 2020 13:01:27 -0700 Subject: [PATCH 1/6] Bring back RN build --- packages/firebase/src/index.rn.ts | 1 + packages/firestore/externs.json | 1 + packages/firestore/memory/package.json | 1 + packages/firestore/package.json | 1 + packages/firestore/rollup.config.es2017.js | 27 ++++++- packages/firestore/src/platform/rn/base64.ts | 22 ++++-- .../firestore/test/unit/util/base64.test.ts | 79 +++++++++++++++++++ 7 files changed, 125 insertions(+), 7 deletions(-) create mode 100644 packages/firestore/test/unit/util/base64.test.ts diff --git a/packages/firebase/src/index.rn.ts b/packages/firebase/src/index.rn.ts index c89e721d0b6..c6e3c6f11bc 100644 --- a/packages/firebase/src/index.rn.ts +++ b/packages/firebase/src/index.rn.ts @@ -23,6 +23,7 @@ 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/externs.json b/packages/firestore/externs.json index 74d91e8f910..07007db0f57 100644 --- a/packages/firestore/externs.json +++ b/packages/firestore/externs.json @@ -1,6 +1,7 @@ { "externs" : [ "node_modules/@types/node/base.d.ts", + "node_modules/@types/node/globals.d.ts", "node_modules/typescript/lib/lib.es5.d.ts", "node_modules/typescript/lib/lib.dom.d.ts", "node_modules/typescript/lib/lib.es2015.promise.d.ts", 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 8b9369638a3..b475af88563 100644 --- a/packages/firestore/package.json +++ b/packages/firestore/package.json @@ -43,6 +43,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", diff --git a/packages/firestore/rollup.config.es2017.js b/packages/firestore/rollup.config.es2017.js index 19f0e83b59c..531098368e5 100644 --- a/packages/firestore/rollup.config.es2017.js +++ b/packages/firestore/rollup.config.es2017.js @@ -108,6 +108,31 @@ const reactNativeBuildPlugins = [ ...browserBuildPlugins.slice(1) ]; +const reactNativeBuilds = [ + // Persistence build + { + input: 'index.rn.ts', + output: { + file: pkg['react-native'], + format: 'es', + sourcemap: true + }, + plugins: reactNativeBuildPlugins, + external: resolveBrowserExterns + }, + // Memory-only build + { + input: 'index.rn.memory.ts', + output: { + file: path.resolve('./memory', memoryPkg['react-native']), + format: 'es', + sourcemap: true + }, + plugins: reactNativeBuildPlugins, + external: resolveBrowserExterns + } +]; + // MARK: Node builds const nodeBuildPlugins = [ @@ -154,4 +179,4 @@ const nodeBuilds = [ } ]; -export default [...browserBuilds, ...nodeBuilds]; +export default [...browserBuilds, ...reactNativeBuilds, ...nodeBuilds]; diff --git a/packages/firestore/src/platform/rn/base64.ts b/packages/firestore/src/platform/rn/base64.ts index 49f6e78a212..4032190e5cd 100644 --- a/packages/firestore/src/platform/rn/base64.ts +++ b/packages/firestore/src/platform/rn/base64.ts @@ -17,18 +17,28 @@ import { base64 } from '@firebase/util'; +// WebSafe uses a different URL-encoding safe alphabet that doesn't match +// the encoding used on the backend. +const WEB_SAFE = false; + /** Converts a Base64 encoded string to a binary string. */ export function decodeBase64(encoded: string): string { - // WebSafe uses a different URL-encoding safe alphabet that doesn't match - // the encoding used on the backend. - return base64.decodeString(encoded, /* webSafe =*/ false); + return String.fromCharCode.apply( + null, + // We use `decodeStringToByteArray()` instead of `decodeString()` since + // `decodeString()` returns Unicode strings, which doesn't match the values + // returned by `atob()`'s Latin1 representation. + base64.decodeStringToByteArray(encoded, WEB_SAFE) + ); } /** Converts a binary string to a Base64 encoded string. */ export function encodeBase64(raw: string): string { - // WebSafe uses a different URL-encoding safe alphabet that doesn't match - // the encoding used on the backend. - return base64.encodeString(raw, /* webSafe =*/ false); + const bytes: number[] = []; + for (let i = 0; i < raw.length; i++) { + bytes[i] = raw.charCodeAt(i); + } + return base64.encodeByteArray(bytes, WEB_SAFE); } /** True if and only if the Base64 conversion functions are available. */ diff --git a/packages/firestore/test/unit/util/base64.test.ts b/packages/firestore/test/unit/util/base64.test.ts new file mode 100644 index 00000000000..bcffd2d69dd --- /dev/null +++ b/packages/firestore/test/unit/util/base64.test.ts @@ -0,0 +1,79 @@ +/** + * @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 { expect } from 'chai'; + +import * as rn from '../../../src/platform/rn/base64'; + +const BASE64_ENCODED = 'GRBoQgKB9LW1'; +const BASE64_DECODED = '\u0019\u0010\u0068\u0042\u0002\u0081\u00f4\u00b5\u00b5'; + +describe('atob', () => { + (typeof atob !== 'undefined' ? it : it.skip)( + 'decodes with native support', + () => { + const decoded = atob(BASE64_ENCODED); + expect(decoded).to.equal(BASE64_DECODED); + } + ); + + (typeof atob !== 'undefined' ? it : it.skip)( + 'roundtrips with native support', + () => { + expect(atob(btoa(BASE64_ENCODED))).to.equal(BASE64_ENCODED); + } + ); + + it('decodes with polyfill', () => { + const decoded = rn.decodeBase64(BASE64_ENCODED); + expect(decoded).to.equal(BASE64_DECODED); + }); + + it('roundtrips with polyfill', () => { + expect(rn.encodeBase64(rn.decodeBase64(BASE64_ENCODED))).to.equal( + BASE64_ENCODED + ); + }); +}); + +describe('btoa', () => { + (typeof btoa !== 'undefined' ? it : it.skip)( + 'encodes with native support', + () => { + const encoded = btoa(BASE64_DECODED); + expect(encoded).to.equal(BASE64_ENCODED); + } + ); + + (typeof btoa !== 'undefined' ? it : it.skip)( + 'roundtrips with native support', + () => { + expect(atob(btoa(BASE64_DECODED))).to.equal(BASE64_DECODED); + } + ); + + it('encodes with polyfill', () => { + const encoded = rn.encodeBase64(BASE64_DECODED); + expect(encoded).to.equal(BASE64_ENCODED); + }); + + it('roundtrips with polyfill', () => { + expect(rn.decodeBase64(rn.encodeBase64(BASE64_DECODED))).to.equal( + BASE64_DECODED + ); + }); +}); From 681c50064d5dc45ca50ba49f17b5dbdbb18accbd Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Mon, 22 Jun 2020 16:25:03 -0700 Subject: [PATCH 2/6] ESM build --- packages/firestore/memory/package.json | 2 +- packages/firestore/package.json | 2 +- packages/firestore/rollup.config.es2017.js | 4 ++-- packages/firestore/rollup.config.es5.js | 23 ++++++++++++++++++++-- 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/packages/firestore/memory/package.json b/packages/firestore/memory/package.json index 04a27eb839a..437b7e11450 100644 --- a/packages/firestore/memory/package.json +++ b/packages/firestore/memory/package.json @@ -3,7 +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", + "react-native": "../dist/index.memory.rn.esm.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 edbc77d7449..80485626f89 100644 --- a/packages/firestore/package.json +++ b/packages/firestore/package.json @@ -43,7 +43,7 @@ }, "main": "dist/index.node.cjs.js", "main-esm2017": "dist/index.node.esm2017.js", - "react-native": "dist/index.rn.esm2017.js", + "react-native": "dist/index.rn.esm.js", "browser": "dist/index.cjs.js", "module": "dist/index.esm.js", "esm2017": "dist/index.esm2017.js", diff --git a/packages/firestore/rollup.config.es2017.js b/packages/firestore/rollup.config.es2017.js index 531098368e5..5b184ea01cd 100644 --- a/packages/firestore/rollup.config.es2017.js +++ b/packages/firestore/rollup.config.es2017.js @@ -113,7 +113,7 @@ const reactNativeBuilds = [ { input: 'index.rn.ts', output: { - file: pkg['react-native'], + file: 'dist/index.rn.esm2017.js', format: 'es', sourcemap: true }, @@ -124,7 +124,7 @@ const reactNativeBuilds = [ { input: 'index.rn.memory.ts', output: { - file: path.resolve('./memory', memoryPkg['react-native']), + file: 'dist/index.memory.rn.esm2017.js', format: 'es', sourcemap: true }, diff --git a/packages/firestore/rollup.config.es5.js b/packages/firestore/rollup.config.es5.js index 93021449936..08f1a9bf114 100644 --- a/packages/firestore/rollup.config.es5.js +++ b/packages/firestore/rollup.config.es5.js @@ -1,6 +1,6 @@ /** * @license - * Copyright 2018 Google Inc. + * Copyright 2018 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -95,6 +95,25 @@ const browserBuilds = [ } ]; +const reactNativeBuilds = [ + { + input: 'dist/index.rn.esm2017.js', + output: { file: pkg['react-native'], format: 'es', sourcemap: true }, + plugins: browserPlugins, + external: resolveBrowserExterns + }, + { + input: 'dist/index.memory.rn.esm2017.js', + output: { + file: path.resolve('./memory', memoryPkg['react-native']), + format: 'es', + sourcemap: true + }, + plugins: browserPlugins, + external: resolveBrowserExterns + } +]; + const nodeBuilds = [ { input: pkg['main-esm2017'], @@ -116,4 +135,4 @@ const nodeBuilds = [ } ]; -export default [...browserBuilds, ...nodeBuilds]; +export default [...browserBuilds, ...reactNativeBuilds, ...nodeBuilds]; From 90f93f1fbe48a9ae115aa0aef15b6876e6adc9f2 Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Mon, 22 Jun 2020 16:26:28 -0700 Subject: [PATCH 3/6] Lint --- packages/firestore/test/unit/util/base64.test.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/firestore/test/unit/util/base64.test.ts b/packages/firestore/test/unit/util/base64.test.ts index bcffd2d69dd..59f227a118d 100644 --- a/packages/firestore/test/unit/util/base64.test.ts +++ b/packages/firestore/test/unit/util/base64.test.ts @@ -23,6 +23,7 @@ const BASE64_ENCODED = 'GRBoQgKB9LW1'; const BASE64_DECODED = '\u0019\u0010\u0068\u0042\u0002\u0081\u00f4\u00b5\u00b5'; describe('atob', () => { + // eslint-disable-next-line no-restricted-properties (typeof atob !== 'undefined' ? it : it.skip)( 'decodes with native support', () => { @@ -31,6 +32,7 @@ describe('atob', () => { } ); + // eslint-disable-next-line no-restricted-properties (typeof atob !== 'undefined' ? it : it.skip)( 'roundtrips with native support', () => { @@ -51,6 +53,7 @@ describe('atob', () => { }); describe('btoa', () => { + // eslint-disable-next-line no-restricted-properties (typeof btoa !== 'undefined' ? it : it.skip)( 'encodes with native support', () => { @@ -59,6 +62,7 @@ describe('btoa', () => { } ); + // eslint-disable-next-line no-restricted-properties (typeof btoa !== 'undefined' ? it : it.skip)( 'roundtrips with native support', () => { From 0ae2f63286bebde5cc6f643af94043546f42bacb Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Tue, 23 Jun 2020 09:53:19 -0700 Subject: [PATCH 4/6] Remove ESM build --- packages/firestore/memory/package.json | 2 +- packages/firestore/package.json | 2 +- packages/firestore/rollup.config.es2017.js | 4 ++-- packages/firestore/rollup.config.es5.js | 21 +-------------------- 4 files changed, 5 insertions(+), 24 deletions(-) diff --git a/packages/firestore/memory/package.json b/packages/firestore/memory/package.json index 437b7e11450..04a27eb839a 100644 --- a/packages/firestore/memory/package.json +++ b/packages/firestore/memory/package.json @@ -3,7 +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.esm.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 80485626f89..edbc77d7449 100644 --- a/packages/firestore/package.json +++ b/packages/firestore/package.json @@ -43,7 +43,7 @@ }, "main": "dist/index.node.cjs.js", "main-esm2017": "dist/index.node.esm2017.js", - "react-native": "dist/index.rn.esm.js", + "react-native": "dist/index.rn.esm2017.js", "browser": "dist/index.cjs.js", "module": "dist/index.esm.js", "esm2017": "dist/index.esm2017.js", diff --git a/packages/firestore/rollup.config.es2017.js b/packages/firestore/rollup.config.es2017.js index 5b184ea01cd..531098368e5 100644 --- a/packages/firestore/rollup.config.es2017.js +++ b/packages/firestore/rollup.config.es2017.js @@ -113,7 +113,7 @@ const reactNativeBuilds = [ { input: 'index.rn.ts', output: { - file: 'dist/index.rn.esm2017.js', + file: pkg['react-native'], format: 'es', sourcemap: true }, @@ -124,7 +124,7 @@ const reactNativeBuilds = [ { input: 'index.rn.memory.ts', output: { - file: 'dist/index.memory.rn.esm2017.js', + file: path.resolve('./memory', memoryPkg['react-native']), format: 'es', sourcemap: true }, diff --git a/packages/firestore/rollup.config.es5.js b/packages/firestore/rollup.config.es5.js index 08f1a9bf114..701ab5d3029 100644 --- a/packages/firestore/rollup.config.es5.js +++ b/packages/firestore/rollup.config.es5.js @@ -95,25 +95,6 @@ const browserBuilds = [ } ]; -const reactNativeBuilds = [ - { - input: 'dist/index.rn.esm2017.js', - output: { file: pkg['react-native'], format: 'es', sourcemap: true }, - plugins: browserPlugins, - external: resolveBrowserExterns - }, - { - input: 'dist/index.memory.rn.esm2017.js', - output: { - file: path.resolve('./memory', memoryPkg['react-native']), - format: 'es', - sourcemap: true - }, - plugins: browserPlugins, - external: resolveBrowserExterns - } -]; - const nodeBuilds = [ { input: pkg['main-esm2017'], @@ -135,4 +116,4 @@ const nodeBuilds = [ } ]; -export default [...browserBuilds, ...reactNativeBuilds, ...nodeBuilds]; +export default [...browserBuilds, ...nodeBuilds]; From 61ccd2f6d636ad3df5112627cc1f7e2263044d30 Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Tue, 23 Jun 2020 09:39:22 -0700 Subject: [PATCH 5/6] Expose static members of public types in ESM2017 build --- packages/firestore/src/util/api.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/firestore/src/util/api.ts b/packages/firestore/src/util/api.ts index 171dfe449c2..984217d2e39 100644 --- a/packages/firestore/src/util/api.ts +++ b/packages/firestore/src/util/api.ts @@ -17,6 +17,9 @@ import { Code, FirestoreError } from './error'; +/** List of JavaScript built-in members that cannot be reassigned. */ +const RESERVED_READONLY_PROPS = ['length', 'name']; + /** * Helper function to prevent instantiation through the constructor. * @@ -41,11 +44,15 @@ export function makeConstructorPrivate( throw new FirestoreError(Code.INVALID_ARGUMENT, error); } - // Make sure instanceof checks work and all methods are exposed on the public - // constructor - PublicConstructor.prototype = cls.prototype; + // Copy static members and prototype + for (const staticProp of Object.getOwnPropertyNames(cls)) { + if (RESERVED_READONLY_PROPS.indexOf(staticProp) === -1) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (PublicConstructor as any)[staticProp] = (cls as any)[staticProp]; + } + } - // Copy any static methods/members + // Copy instance methods/members Object.assign(PublicConstructor, cls); // eslint-disable-next-line @typescript-eslint/no-explicit-any From f64a733b0d4302db74040df079a71b6597660310 Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Tue, 30 Jun 2020 10:07:26 -0700 Subject: [PATCH 6/6] Create shy-forks-speak.md --- .changeset/shy-forks-speak.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/shy-forks-speak.md diff --git a/.changeset/shy-forks-speak.md b/.changeset/shy-forks-speak.md new file mode 100644 index 00000000000..f840fc2e7d1 --- /dev/null +++ b/.changeset/shy-forks-speak.md @@ -0,0 +1,6 @@ +--- +"firebase": minor +"@firebase/firestore": minor +--- + +Re-adding the ReactNative bundle, which allows Firestore to be used without `btoa`/`atob` Polyfills.