Skip to content

Commit

Permalink
Share code with Admin SDK properly (#5537)
Browse files Browse the repository at this point in the history
* Add standalone entry point in database-compat

* add exports field

* fix lint

* Create afraid-boxes-roll.md

* address comments

* add an emptry line to the end of file
  • Loading branch information
Feiyang1 authored Sep 24, 2021
1 parent e62d02e commit dfe65ff
Show file tree
Hide file tree
Showing 12 changed files with 276 additions and 165 deletions.
6 changes: 6 additions & 0 deletions .changeset/afraid-boxes-roll.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@firebase/database-compat": patch
"@firebase/database": patch
---

Added an entry point `@firebase/database-compat/standalone` to share code with Admin SDK properly
13 changes: 13 additions & 0 deletions packages/database-compat/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,19 @@
"esm5": "dist/index.esm5.js",
"license": "Apache-2.0",
"typings": "dist/database-compat/src/index.d.ts",
"files": [
"dist",
"standalone/package.json"
],
"exports": {
".": {
"node": "./dist/index.js",
"default": "./dist/index.esm2017.js"
},
"./standalone": {
"node": "./dist/index.standalone.js"
}
},
"scripts": {
"lint": "eslint -c .eslintrc.js '**/*.ts' --ignore-path '../../.gitignore'",
"lint:fix": "eslint --fix -c .eslintrc.js '**/*.ts' --ignore-path '../../.gitignore'",
Expand Down
34 changes: 31 additions & 3 deletions packages/database-compat/rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@
import json from '@rollup/plugin-json';
import typescriptPlugin from 'rollup-plugin-typescript2';
import typescript from 'typescript';
import commonjs from '@rollup/plugin-commonjs';
import resolveModule from '@rollup/plugin-node-resolve';

import pkg from './package.json';
import standalonePkg from './standalone/package.json';

const deps = Object.keys(
Object.assign({}, pkg.peerDependencies, pkg.dependencies)
);
const deps = Object.keys({ ...pkg.peerDependencies, ...pkg.dependencies });

function onWarn(warning, defaultWarn) {
if (warning.code === 'CIRCULAR_DEPENDENCY') {
Expand Down Expand Up @@ -81,6 +82,33 @@ const es5Builds = [
},
external: id => deps.some(dep => id === dep || id.startsWith(`${dep}/`)),
onwarn: onWarn
},
/**
* Standalone Build (used by Admin SDK).
* @firebase/database and only @firebase/database is bundled in this build.
*/
{
input: 'src/index.standalone.ts',
output: [
{
file: standalonePkg.main.replace('../', ''),
format: 'cjs',
sourcemap: true
}
],
plugins: [
...es5BuildPlugins,
resolveModule({
mainFields: ['standalone'],
preferBuiltins: true
}),
commonjs()
],
treeshake: {
moduleSideEffects: false
},
external: id => deps.filter(dep => dep !== '@firebase/database').some(dep => id === dep || id.startsWith(`${dep}/`)),
onwarn: onWarn
}
];

Expand Down
73 changes: 5 additions & 68 deletions packages/database-compat/src/index.node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { FirebaseApp, FirebaseNamespace } from '@firebase/app-types';
import firebase from '@firebase/app-compat';
import { FirebaseNamespace } from '@firebase/app-types';
import { _FirebaseNamespace } from '@firebase/app-types/private';
import { FirebaseAuthInternal } from '@firebase/auth-interop-types';
import { Component, ComponentType } from '@firebase/component';
import { enableLogging } from '@firebase/database';
import * as types from '@firebase/database-types';
import { CONSTANTS, isNodeSdk } from '@firebase/util';

import { name, version } from '../package.json';
import { Database } from '../src/api/Database';
Expand All @@ -30,45 +28,9 @@ import { DataSnapshot, Query, Reference } from '../src/api/Reference';

const ServerValue = Database.ServerValue;

/**
* A one off register function which returns a database based on the app and
* passed database URL. (Used by the Admin SDK)
*
* @param app - A valid FirebaseApp-like object
* @param url - A valid Firebase databaseURL
* @param version - custom version e.g. firebase-admin version
* @param nodeAdmin - true if the SDK is being initialized from Firebase Admin.
*/
export function initStandalone(
app: FirebaseApp,
url: string,
version: string,
nodeAdmin = true
) {
CONSTANTS.NODE_ADMIN = nodeAdmin;
return INTERNAL.initStandalone({
app,
url,
version,
// firebase-admin-node's app.INTERNAL implements FirebaseAuthInternal interface
// eslint-disable-next-line @typescript-eslint/no-explicit-any
customAuthImpl: (app as any).INTERNAL as FirebaseAuthInternal,
namespace: {
Reference,
Query,
Database,
DataSnapshot,
enableLogging,
INTERNAL,
ServerValue
},
nodeAdmin
});
}

export function registerDatabase(instance: FirebaseNamespace) {
function registerDatabase(instance: FirebaseNamespace) {
// Register the Database Service with the 'firebase' namespace.
const namespace = (instance as _FirebaseNamespace).INTERNAL.registerComponent(
(instance as _FirebaseNamespace).INTERNAL.registerComponent(
new Component(
'database-compat',
(container, { instanceIdentifier: url }) => {
Expand Down Expand Up @@ -98,33 +60,9 @@ export function registerDatabase(instance: FirebaseNamespace) {
);

instance.registerVersion(name, version, 'node');

if (isNodeSdk()) {
module.exports = Object.assign({}, namespace, { initStandalone });
}
}

try {
// If @firebase/app is not present, skip registering database.
// It could happen when this package is used in firebase-admin which doesn't depend on @firebase/app.
// Previously firebase-admin depends on @firebase/app, which causes version conflict on
// @firebase/app when used together with the js sdk. More detail:
// https://github.com/firebase/firebase-js-sdk/issues/1696#issuecomment-501546596
// eslint-disable-next-line import/no-extraneous-dependencies, @typescript-eslint/no-require-imports
const firebase = require('@firebase/app-compat').default;
registerDatabase(firebase);
} catch (err) {
// catch and ignore 'MODULE_NOT_FOUND' error in firebase-admin context
// we can safely ignore this error because RTDB in firebase-admin works without @firebase/app
if (err.code !== 'MODULE_NOT_FOUND') {
throw err;
}
}

// Types to export for the admin SDK
export { Database, Query, Reference, enableLogging, ServerValue };

export { OnDisconnect } from '@firebase/database';
registerDatabase(firebase);

declare module '@firebase/app-compat' {
interface FirebaseNamespace {
Expand All @@ -139,4 +77,3 @@ declare module '@firebase/app-compat' {
database?(): types.FirebaseDatabase;
}
}
export { DataSnapshot } from '../src/api/Reference';
68 changes: 68 additions & 0 deletions packages/database-compat/src/index.standalone.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* @license
* Copyright 2021 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 { FirebaseApp } from '@firebase/app-types';
import { FirebaseAuthInternal } from '@firebase/auth-interop-types';
import { enableLogging } from '@firebase/database';
import { CONSTANTS } from '@firebase/util';

import { Database } from './api/Database';
import * as INTERNAL from './api/internal';
import { DataSnapshot, Query, Reference } from './api/Reference';

const ServerValue = Database.ServerValue;

/**
* A one off register function which returns a database based on the app and
* passed database URL. (Used by the Admin SDK)
*
* @param app - A valid FirebaseApp-like object
* @param url - A valid Firebase databaseURL
* @param version - custom version e.g. firebase-admin version
* @param nodeAdmin - true if the SDK is being initialized from Firebase Admin.
*/
export function initStandalone(
app: FirebaseApp,
url: string,
version: string,
nodeAdmin = true
) {
CONSTANTS.NODE_ADMIN = nodeAdmin;
return INTERNAL.initStandalone({
app,
url,
version,
// firebase-admin-node's app.INTERNAL implements FirebaseAuthInternal interface
// eslint-disable-next-line @typescript-eslint/no-explicit-any
customAuthImpl: (app as any).INTERNAL as FirebaseAuthInternal,
namespace: {
Reference,
Query,
Database,
DataSnapshot,
enableLogging,
INTERNAL,
ServerValue
},
nodeAdmin
});
}

// Types to export for the admin SDK
export { Database, Query, Reference, enableLogging, ServerValue };
export { OnDisconnect } from '@firebase/database';
export { DataSnapshot } from './api/Reference';
22 changes: 8 additions & 14 deletions packages/database-compat/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,15 @@ import { enableLogging } from '@firebase/database';
import * as types from '@firebase/database-types';

import { name, version } from '../package.json';
import { Database as DatabaseCompat } from '../src/api/Database';
import { Database } from '../src/api/Database';
import * as INTERNAL from '../src/api/internal';
// rename the imports to avoid conflicts with imports that will be added by "yarn add-compat-overloads" during a release build
import { DataSnapshot as DataSnapshotCompat, Query as QueryCompat, Reference } from '../src/api/Reference';
import { DataSnapshot, Query, Reference } from '../src/api/Reference';

const ServerValue = DatabaseCompat.ServerValue;
const ServerValue = Database.ServerValue;

export function registerDatabase(instance: FirebaseNamespace) {
// Register the Database Service with the 'firebase' namespace.
const namespace = (
(
instance as unknown as _FirebaseNamespace
).INTERNAL.registerComponent(
new Component(
Expand All @@ -44,17 +43,17 @@ export function registerDatabase(instance: FirebaseNamespace) {
const databaseExp = container
.getProvider('database')
.getImmediate({ identifier: url });
return new DatabaseCompat(databaseExp, app);
return new Database(databaseExp, app);
},
ComponentType.PUBLIC
)
.setServiceProps(
// firebase.database namespace properties
{
Reference,
Query: QueryCompat,
Database: DatabaseCompat,
DataSnapshot: DataSnapshotCompat,
Query,
Database,
DataSnapshot,
enableLogging,
INTERNAL,
ServerValue
Expand All @@ -68,11 +67,6 @@ export function registerDatabase(instance: FirebaseNamespace) {

registerDatabase(firebase);

// Types to export for the admin SDK. They are exported here in the browser entry point only for types
// The same symbol should be exported from the node entry point so their values can be accessed at runtime by admin SDK
export { DatabaseCompat as Database, QueryCompat as Query, Reference, enableLogging, ServerValue, DataSnapshotCompat as DataSnapshot };
export { OnDisconnect } from '@firebase/database';

declare module '@firebase/app-compat' {
interface FirebaseNamespace {
database?: {
Expand Down
7 changes: 7 additions & 0 deletions packages/database-compat/standalone/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "@firebase/database-compat/standalone",
"description": "The entry point for sharing code with Admin SDK",
"main": "../dist/index.standalone.js",
"typings": "../dist/database-compat/src/index.standalone.d.ts",
"private": true
}
1 change: 1 addition & 0 deletions packages/database/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"browser": "dist/index.esm2017.js",
"module": "dist/index.esm2017.js",
"esm5": "dist/index.esm5.js",
"standalone": "dist/index.standalone.js",
"files": [
"dist"
],
Expand Down
15 changes: 14 additions & 1 deletion packages/database/rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,20 @@ const es5Builds = [
},
external: id => deps.some(dep => id === dep || id.startsWith(`${dep}/`)),
onwarn: onWarn
}
},
/**
* Standalone Build
*/
{
input: 'src/index.standalone.ts',
output: [{ file: pkg.standalone, format: 'cjs', sourcemap: true }],
plugins: es5BuildPlugins,
treeshake: {
moduleSideEffects: false
},
external: id => deps.some(dep => id === dep || id.startsWith(`${dep}/`)),
onwarn: onWarn
},
];

/**
Expand Down
Loading

0 comments on commit dfe65ff

Please sign in to comment.