Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions packages/util/defaults.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { FirebaseOptions } from "@firebase/app-types";

declare const FirebaseDefaults: {
config?: FirebaseOptions;
emulatorHosts?: Record<string, string|undefined>;
} | undefined;

export default FirebaseDefaults;

Check failure on line 8 in packages/util/defaults.d.ts

View workflow job for this annotation

GitHub Actions / Lint

Prefer named exports
1 change: 1 addition & 0 deletions packages/util/defaults.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = undefined
1 change: 1 addition & 0 deletions packages/util/defaults.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default undefined
14 changes: 12 additions & 2 deletions packages/util/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,19 @@
},
"default": "./dist/index.esm2017.js"
},
"./postinstall": {
"types": "./defaults.d.ts",
"require": "./defaults.js",
"import": "./defaults.mjs",
"default": "./defaults.mjs"
},
"./package.json": "./package.json"
},
"files": [
"dist"
"dist",
"defaults.*js",
"defaults.d.ts",
"postinstall.mjs"
],
"scripts": {
"lint": "eslint -c .eslintrc.js '**/*.ts' --ignore-path '../../.gitignore'",
Expand All @@ -40,7 +49,8 @@
"test:node": "TS_NODE_CACHE=NO TS_NODE_COMPILER_OPTIONS='{\"module\":\"commonjs\"}' nyc --reporter lcovonly -- mocha test/**/*.test.* --config ../../config/mocharc.node.js",
"trusted-type-check": "tsec -p tsconfig.json --noEmit",
"api-report": "api-extractor run --local --verbose",
"typings:public": "node ../../scripts/build/use_typings.js ./dist/util-public.d.ts"
"typings:public": "node ../../scripts/build/use_typings.js ./dist/util-public.d.ts",
"postinstall": "node ./postinstall.mjs"
},
"license": "Apache-2.0",
"dependencies": {
Expand Down
98 changes: 98 additions & 0 deletions packages/util/postinstall.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { writeFile, readFile } from "node:fs/promises";
import { pathToFileURL } from "node:url";
import { isAbsolute, join } from "node:path";

async function getWebConfig() {
let configFromEnvironment = undefined;
// $FIREBASE_WEBAPP_CONFIG can be either a JSON representation of FirebaseOptions or the path
// to a filename
if (process.env.FIREBASE_WEBAPP_CONFIG) {
if (process.env.FIREBASE_WEBAPP_CONFIG.startsWith("{\"")) {
try {
configFromEnvironment = JSON.parse(process.env.FIREBASE_WEBAPP_CONFIG);
} catch(e) {
console.error("FIREBASE_WEBAPP_CONFIG could not be parsed.", e);
}
} if (process.env.FIREBASE_WEBAPP_CONFIG.startsWith("{")) {
// TODO temporary
configFromEnvironment = Object.fromEntries(
process.env.FIREBASE_WEBAPP_CONFIG
.match(/^{(.+)}$/)[1]
.split(',')
.map(it => it.match("([^\:]+)\:(.+)")?.slice(1))
.filter(it => it)
);
} else {
const fileName = process.env.FIREBASE_WEBAPP_CONFIG;
const fileURL = pathToFileURL(isAbsolute(fileName) ? fileName : join(process.cwd(), fileName));
const fileContents = await readFile(fileURL, "utf-8").catch((err) => {
console.error(err);
return undefined;
});
if (fileContents) {
try {
configFromEnvironment = JSON.parse(fileContents);
} catch(e) {
console.error(`Contents of ${fileName} could not be parsed.`, e);
}
}
}
}

// In Firebase App Hosting the config provided to the environment variable is up-to-date and
// "complete" we should not reach out to the webConfig endpoint to freshen it
if (process.env.X_GOOGLE_TARGET_PLATFORM === "fah") {
return configFromEnvironment;
}

if (!configFromEnvironment) {
return undefined;
}
const projectId = configFromEnvironment.projectId || "-";
const appId = configFromEnvironment.appId;
const apiKey = configFromEnvironment.apiKey;
if (!appId || !apiKey) {
console.error("appId and apiKey are needed");
return undefined;
}
const response = await fetch(
`https://firebase.googleapis.com/v1alpha/projects/${projectId}/apps/${appId}/webConfig`,
{ headers: { "x-goog-api-key": apiKey } }
).catch((e) => {
// TODO add sensible error
console.error(e);
return configFromEnvironment;
});
if (!response.ok) {
// TODO add sensible error
console.error("yikes.");
return configFromEnvironment;
}
const json = await response.json().catch(() => {
// TODO add sensible error
console.error("also yikes.");
return configFromEnvironment;
});
return { ...json, apiKey };
}

const config = await getWebConfig();

const emulatorHosts = {
// TODO: remote config, functions, and data connect emulators?
firestore: process.env.FIRESTORE_EMULATOR_HOST,
database: process.env.FIREBASE_DATABASE_EMULATOR_HOST,
storage: process.env.FIREBASE_STORAGE_EMULATOR_HOST,
auth: process.env.FIREBASE_AUTH_EMULATOR_HOST,
};

const anyEmulatorHosts = Object.values(emulatorHosts).filter(it => it).length > 0;

// getDefaults() will use this object, rather than fallback to other autoinit suppliers, if it's
// truthy—if we've done nothing here, make it falsy.
const defaults = (config || anyEmulatorHosts) ? { config, emulatorHosts } : undefined;

await Promise.all([
writeFile(join(import.meta.dirname, "defaults.js"), `module.exports = ${JSON.stringify(defaults)}`),
writeFile(join(import.meta.dirname, "defaults.mjs"), `export default ${JSON.stringify(defaults)}`),
]);
3 changes: 3 additions & 0 deletions packages/util/src/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

import { base64Decode } from './crypt';
import { getGlobal } from './global';
// @ts-ignore
import postinstallDefaults from "@firebase/util/postinstall";

Check failure on line 21 in packages/util/src/defaults.ts

View workflow job for this annotation

GitHub Actions / Lint

'@firebase/util' should be listed in the project's dependencies. Run 'npm i -S @firebase/util' to add it

/**
* Keys for experimental properties on the `FirebaseDefaults` object.
Expand Down Expand Up @@ -100,6 +102,7 @@
export const getDefaults = (): FirebaseDefaults | undefined => {
try {
return (
postinstallDefaults ||
getDefaultsFromGlobal() ||
getDefaultsFromEnvVariable() ||
getDefaultsFromCookie()
Expand Down
Loading