Skip to content

Commit cd6d61a

Browse files
committed
Making postinstall.mjs more robust
1 parent 6470299 commit cd6d61a

File tree

1 file changed

+70
-26
lines changed

1 file changed

+70
-26
lines changed

packages/util/postinstall.mjs

Lines changed: 70 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,85 @@
1-
import { writeFile } from "fs/promises";
2-
3-
let firebaseConfig = {};
4-
if (process.env.FIREBASE_CONFIG?.startsWith("{")) {
5-
// TODO probably want a more robust yaml parse
6-
firebaseConfig = Object.fromEntries(process.env.FIREBASE_CONFIG.match(/[^(\:\{\},)]+\:[^(,})]+/g).map(it => {
7-
const parts = it.split(":");
8-
return [parts[0], parts.slice(1).join(":")]
9-
}));
10-
}
1+
import { writeFile, readFile } from "node:fs/promises";
2+
import { pathToFileURL } from "node:url";
3+
import { isAbsolute, join } from "node:path";
114

12-
const projectId = firebaseConfig.projectId;
13-
const appId = firebaseConfig.appId;
14-
const apiKey = firebaseConfig.apiKey;
5+
async function getWebConfig() {
6+
let configFromEnvironment = undefined;
7+
// $FIREBASE_WEBAPP_CONFIG can be either a JSON representation of FirebaseOptions or the path
8+
// to a filename
9+
if (process.env.FIREBASE_WEBAPP_CONFIG) {
10+
if (process.env.FIREBASE_WEBAPP_CONFIG.startsWith("{")) {
11+
try {
12+
configFromEnvironment = JSON.parse(process.env.FIREBASE_WEBAPP_CONFIG);
13+
} catch(e) {
14+
console.error("FIREBASE_WEBAPP_CONFIG could not be parsed.", e);
15+
}
16+
} else {
17+
const fileName = process.env.FIREBASE_WEBAPP_CONFIG;
18+
const fileURL = pathToFileURL(isAbsolute(fileName) ? fileName : join(process.cwd(), fileName));
19+
const fileContents = await readFile(fileURL, "utf-8").catch((err) => {
20+
console.error(err);
21+
return undefined;
22+
});
23+
if (fileContents) {
24+
try {
25+
configFromEnvironment = JSON.parse(fileContents);
26+
} catch(e) {
27+
console.error(`Contents of ${fileName} could not be parsed.`, e);
28+
}
29+
}
30+
}
31+
}
1532

16-
const config = projectId && appId && apiKey && await (await fetch(
17-
`https://firebase.googleapis.com/v1alpha/projects/${projectId}/apps/${appId}/webConfig`,
18-
{ headers: { "x-goog-api-key": apiKey } }
19-
)).json();
33+
// In Firebase App Hosting the config provided to the environment variable is up-to-date and
34+
// "complete" we should not reach out to the webConfig endpoint to freshen it
35+
if (process.env.X_GOOGLE_TARGET_PLATFORM === "fah") {
36+
return configFromEnvironment;
37+
}
2038

21-
if (config) {
22-
config.apiKey = apiKey;
39+
if (!configFromEnvironment) {
40+
return undefined;
41+
}
42+
const projectId = configFromEnvironment.projectId || "-";
43+
const appId = configFromEnvironment.appId;
44+
const apiKey = configFromEnvironment.apiKey;
45+
if (!appId || !apiKey) {
46+
console.error("appId and apiKey are needed");
47+
return undefined;
48+
}
49+
const response = await fetch(
50+
`https://firebase.googleapis.com/v1alpha/projects/${projectId}/apps/${appId}/webConfig`,
51+
{ headers: { "x-goog-api-key": apiKey } }
52+
);
53+
if (!response.ok) {
54+
// TODO add sensible error
55+
console.error("yikes.");
56+
return undefined;
57+
}
58+
const json = await response.json().catch(() => {
59+
// TODO add sensible error
60+
console.error("also yikes.");
61+
return undefined;
62+
});
63+
return { ...json, apiKey };
2364
}
2465

25-
let emulatorHosts = {
66+
const config = await getWebConfig();
67+
68+
const emulatorHosts = {
69+
// TODO: remote config, functions, and data connect emulators?
2670
firestore: process.env.FIRESTORE_EMULATOR_HOST,
2771
database: process.env.FIREBASE_DATABASE_EMULATOR_HOST,
2872
storage: process.env.FIREBASE_STORAGE_EMULATOR_HOST,
2973
auth: process.env.FIREBASE_AUTH_EMULATOR_HOST,
3074
};
3175

32-
if (!Object.values(emulatorHosts).filter(it => it).length) {
33-
emulatorHosts = undefined;
34-
}
76+
const anyEmulatorHosts = Object.values(emulatorHosts).filter(it => it).length > 0;
3577

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

3882
await Promise.all([
39-
writeFile("./defaults.js", `module.exports = ${JSON.stringify(defaults)}`),
40-
writeFile("./defaults.mjs", `export default ${JSON.stringify(defaults)}`),
83+
writeFile(join(import.meta.dirname, "defaults.js"), `module.exports = ${JSON.stringify(defaults)}`),
84+
writeFile(join(import.meta.dirname, "defaults.mjs"), `export default ${JSON.stringify(defaults)}`),
4185
]);

0 commit comments

Comments
 (0)