firebase-tools 15.9.1 → 15.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,15 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.askQuestions = askQuestions;
4
4
  exports.actuate = actuate;
5
5
  const clc = require("colorette");
6
- const node_fs_1 = require("node:fs");
7
6
  const path_1 = require("path");
8
7
  const apiv2_1 = require("../../../apiv2");
9
- const github_1 = require("./github");
8
+ const frameworks_1 = require("../../../frameworks");
9
+ const github = require("./github");
10
10
  const prompt_1 = require("../../../prompt");
11
11
  const logger_1 = require("../../../logger");
12
- const frameworks_1 = require("../../../frameworks");
13
- const constants_1 = require("../../../frameworks/constants");
14
- const experiments = require("../../../experiments");
15
12
  const getDefaultHostingSite_1 = require("../../../getDefaultHostingSite");
16
13
  const utils_1 = require("../../../utils");
17
14
  const interactive_1 = require("../../../hosting/interactive");
@@ -22,7 +19,51 @@ const INDEX_TEMPLATE = (0, templates_1.readTemplateSync)("init/hosting/index.htm
22
19
  const MISSING_TEMPLATE = (0, templates_1.readTemplateSync)("init/hosting/404.html");
23
20
  const DEFAULT_IGNORES = ["firebase.json", "**/.*", "**/node_modules/**"];
24
21
  async function askQuestions(setup, config, options) {
25
- var _a, _b, _c, _d, _e;
22
+ var _a, _b;
23
+ const discoveredFramework = await (0, frameworks_1.discover)(config.projectDir, false);
24
+ if (discoveredFramework && discoveredFramework.mayWantBackend) {
25
+ const frameworkName = frameworks_1.WebFrameworks[discoveredFramework.framework]?.name ?? discoveredFramework.framework;
26
+ switch (discoveredFramework.framework) {
27
+ case "next":
28
+ case "angular":
29
+ case "nuxt":
30
+ case "nuxt2":
31
+ case "express":
32
+ case "svelekit":
33
+ case "sveltekit":
34
+ logger_1.logger.info();
35
+ const useAppHosting = await (0, prompt_1.confirm)({
36
+ message: `Detected a ${frameworkName} codebase with SSR features. We can't guarantee that ` +
37
+ `this site will work on Firebase Hosting, which is optimized for static sites. Another ` +
38
+ `product, Firebase App Hosting, was designed for SSR web apps. Would ` +
39
+ `you like to use App Hosting instead? Learn more here: ` +
40
+ `https://firebase.google.com/docs/app-hosting/product-comparison#hostings`,
41
+ default: true,
42
+ });
43
+ if (useAppHosting) {
44
+ setup.featureInfo || (setup.featureInfo = {});
45
+ setup.featureInfo.hosting = { redirectToAppHosting: true };
46
+ setup.features?.unshift("apphosting");
47
+ return;
48
+ }
49
+ break;
50
+ default:
51
+ logger_1.logger.info();
52
+ logger_1.logger.info(`Detected a ${frameworkName} codebase with SSR features. We can't guarantee that ` +
53
+ `this site will work on Firebase Hosting, which is optimized for static sites. Another ` +
54
+ `product, Firebase App Hosting, was designed for SSR web apps.`);
55
+ logger_1.logger.info(`Learn about App Hosting here: https://firebase.google.com/docs/app-hosting/product-comparison#hostings`);
56
+ logger_1.logger.info(`Learn how to deploy frameworks with App Hosting here: https://firebase.blog/posts/2025/06/app-hosting-frameworks/`);
57
+ const continueWithHosting = await (0, prompt_1.confirm)({
58
+ message: `Would you like to continue setting up Firebase Hosting?`,
59
+ default: false,
60
+ });
61
+ if (!continueWithHosting) {
62
+ throw new error_1.FirebaseError("Hosting initialization cancelled.", { exit: 1 });
63
+ }
64
+ break;
65
+ }
66
+ }
26
67
  setup.featureInfo = setup.featureInfo || {};
27
68
  setup.featureInfo.hosting = {};
28
69
  if (setup.projectId) {
@@ -48,75 +89,18 @@ async function askQuestions(setup, config, options) {
48
89
  setup.featureInfo.hosting.newSiteId = await (0, interactive_1.pickHostingSiteName)("", createOptions);
49
90
  }
50
91
  }
51
- let discoveredFramework = experiments.isEnabled("webframeworks")
52
- ? await (0, frameworks_1.discover)(config.projectDir, false)
53
- : undefined;
54
- if (experiments.isEnabled("webframeworks")) {
55
- if (discoveredFramework &&
56
- (await (0, prompt_1.confirm)({
57
- message: `Detected an existing ${frameworks_1.WebFrameworks[discoveredFramework.framework].name} codebase in the current directory, do you want to use this?`,
58
- default: true,
59
- }))) {
60
- setup.featureInfo.hosting.source = ".";
61
- setup.featureInfo.hosting.useWebFrameworks = true;
62
- setup.featureInfo.hosting.useDiscoveredFramework = true;
63
- setup.featureInfo.hosting.webFramework = discoveredFramework.framework;
64
- }
65
- else {
66
- setup.featureInfo.hosting.useWebFrameworks = await (0, prompt_1.confirm)(`Do you want to use a web framework? (${clc.bold("experimental")})`);
67
- }
68
- }
69
- if (setup.featureInfo.hosting.useWebFrameworks) {
70
- (_a = setup.featureInfo.hosting).source ?? (_a.source = await (0, prompt_1.input)({
71
- message: "What folder would you like to use for your web application's root directory?",
72
- default: "hosting",
73
- }));
74
- discoveredFramework = await (0, frameworks_1.discover)((0, path_1.join)(config.projectDir, setup.featureInfo.hosting.source));
75
- if (discoveredFramework) {
76
- const name = frameworks_1.WebFrameworks[discoveredFramework.framework].name;
77
- (_b = setup.featureInfo.hosting).useDiscoveredFramework ?? (_b.useDiscoveredFramework = await (0, prompt_1.confirm)({
78
- message: `Detected an existing ${name} codebase in ${setup.featureInfo.hosting.source}, should we use this?`,
79
- default: true,
80
- }));
81
- if (setup.featureInfo.hosting.useDiscoveredFramework)
82
- setup.featureInfo.hosting.webFramework = discoveredFramework.framework;
83
- }
84
- const choices = [];
85
- for (const value in frameworks_1.WebFrameworks) {
86
- if (frameworks_1.WebFrameworks[value]) {
87
- const { name, init } = frameworks_1.WebFrameworks[value];
88
- if (init)
89
- choices.push({ name, value });
90
- }
91
- }
92
- const defaultChoice = choices.find(({ value }) => value === discoveredFramework?.framework)?.value;
93
- (_c = setup.featureInfo.hosting).webFramework ?? (_c.webFramework = await (0, prompt_1.select)({
94
- message: "Please choose the framework:",
95
- default: defaultChoice,
96
- choices,
97
- }));
98
- setup.featureInfo.hosting.region =
99
- setup.featureInfo.hosting.region ||
100
- (await (0, prompt_1.select)({
101
- message: "In which region would you like to host server-side content, if applicable?",
102
- default: constants_1.DEFAULT_REGION,
103
- choices: constants_1.ALLOWED_SSR_REGIONS.filter((region) => region.recommended),
104
- }));
105
- }
106
- else {
107
- logger_1.logger.info();
108
- logger_1.logger.info(`Your ${clc.bold("public")} directory is the folder (relative to your project directory) that`);
109
- logger_1.logger.info(`will contain Hosting assets to be uploaded with ${clc.bold("firebase deploy")}. If you`);
110
- logger_1.logger.info("have a build process for your assets, use your build's output directory.");
111
- logger_1.logger.info();
112
- (_d = setup.featureInfo.hosting).public ?? (_d.public = await (0, prompt_1.input)({
113
- message: "What do you want to use as your public directory?",
114
- default: "public",
115
- }));
116
- (_e = setup.featureInfo.hosting).spa ?? (_e.spa = await (0, prompt_1.confirm)("Configure as a single-page app (rewrite all urls to /index.html)?"));
117
- }
92
+ logger_1.logger.info();
93
+ logger_1.logger.info(`Your ${clc.bold("public")} directory is the folder (relative to your project directory) that`);
94
+ logger_1.logger.info(`will contain Hosting assets to be uploaded with ${clc.bold("firebase deploy")}. If you`);
95
+ logger_1.logger.info("have a build process for your assets, use your build's output directory.");
96
+ logger_1.logger.info();
97
+ (_a = setup.featureInfo.hosting).public ?? (_a.public = await (0, prompt_1.input)({
98
+ message: "What do you want to use as your public directory?",
99
+ default: "public",
100
+ }));
101
+ (_b = setup.featureInfo.hosting).spa ?? (_b.spa = await (0, prompt_1.confirm)("Configure as a single-page app (rewrite all urls to /index.html)?"));
118
102
  if (await (0, prompt_1.confirm)("Set up automatic builds and deploys with GitHub?")) {
119
- return (0, github_1.initGitHub)(setup);
103
+ return github.initGitHub(setup);
120
104
  }
121
105
  }
122
106
  async function actuate(setup, config, options) {
@@ -124,40 +108,26 @@ async function actuate(setup, config, options) {
124
108
  if (!hostingInfo) {
125
109
  throw new error_1.FirebaseError("Could not find hosting info in setup.featureInfo.hosting. This should not happen.", { exit: 2 });
126
110
  }
111
+ if (hostingInfo.redirectToAppHosting) {
112
+ return;
113
+ }
127
114
  if (hostingInfo.newSiteId && setup.projectId) {
128
115
  await (0, api_1.createSite)(setup.projectId, hostingInfo.newSiteId);
129
116
  logger_1.logger.info();
130
117
  (0, utils_1.logSuccess)(`Firebase Hosting site ${hostingInfo.newSiteId} created!`);
131
118
  logger_1.logger.info();
132
119
  }
133
- if (hostingInfo.webFramework) {
134
- if (!hostingInfo.useDiscoveredFramework) {
135
- if (hostingInfo.source && (0, node_fs_1.existsSync)(hostingInfo.source)) {
136
- (0, node_fs_1.rmSync)(hostingInfo.source, { recursive: true });
137
- }
138
- await frameworks_1.WebFrameworks[hostingInfo.webFramework].init(setup, config);
139
- }
140
- setup.config.hosting = {
141
- source: hostingInfo.source,
142
- ignore: DEFAULT_IGNORES,
143
- frameworksBackend: {
144
- region: hostingInfo.region,
145
- },
146
- };
120
+ setup.config.hosting = {
121
+ public: hostingInfo.public,
122
+ ignore: DEFAULT_IGNORES,
123
+ };
124
+ if (hostingInfo.spa) {
125
+ setup.config.hosting.rewrites = [{ source: "**", destination: "/index.html" }];
147
126
  }
148
127
  else {
149
- setup.config.hosting = {
150
- public: hostingInfo.public,
151
- ignore: DEFAULT_IGNORES,
152
- };
153
- if (hostingInfo.spa) {
154
- setup.config.hosting.rewrites = [{ source: "**", destination: "/index.html" }];
155
- }
156
- else {
157
- await config.askWriteProjectFile(`${hostingInfo.public}/404.html`, MISSING_TEMPLATE, !!options.force);
158
- }
159
- const c = new apiv2_1.Client({ urlPrefix: "https://www.gstatic.com", auth: false });
160
- const response = await c.get("/firebasejs/releases.json");
161
- await config.askWriteProjectFile(`${hostingInfo.public}/index.html`, INDEX_TEMPLATE.replace(/{{VERSION}}/g, response.body.current.version), !!options.force);
128
+ await config.askWriteProjectFile((0, path_1.join)(hostingInfo.public ?? "public", "404.html"), MISSING_TEMPLATE, !!options.force);
162
129
  }
130
+ const c = new apiv2_1.Client({ urlPrefix: "https://www.gstatic.com", auth: false });
131
+ const response = await c.get("/firebasejs/releases.json");
132
+ await config.askWriteProjectFile((0, path_1.join)(hostingInfo.public ?? "public", "index.html"), INDEX_TEMPLATE.replace(/{{VERSION}}/g, response.body.current.version), !!options.force);
163
133
  }
@@ -58,8 +58,8 @@ exports.run_tests = (0, tool_1.tool)("apptesting", {
58
58
  }, async ({ appId, releaseBinaryFile, testDevices, testCase }) => {
59
59
  const devices = testDevices || defaultDevices;
60
60
  const client = new client_1.AppDistributionClient();
61
- const releaseName = await (0, distribution_1.upload)(client, (0, options_parser_util_1.toAppName)(appId), new distribution_1.Distribution(releaseBinaryFile));
62
- return (0, util_1.toContent)(await client.createReleaseTest(releaseName, devices, testCase));
61
+ const release = await (0, distribution_1.upload)(client, (0, options_parser_util_1.toAppName)(appId), new distribution_1.Distribution(releaseBinaryFile));
62
+ return (0, util_1.toContent)(await client.createReleaseTest(release.name, devices, testCase));
63
63
  });
64
64
  exports.check_status = (0, tool_1.tool)("apptesting", {
65
65
  name: "check_status",
package/lib/track.js CHANGED
@@ -33,7 +33,7 @@ exports.GA4_PROPERTIES = {
33
33
  },
34
34
  };
35
35
  function usageEnabled() {
36
- return ((!!process.env.IS_FIREBASE_CLI || !!process.env.IS_FIREBASE_MCP) && !!configstore_1.configstore.get("usage"));
36
+ return (!!process.env.IS_FIREBASE_CLI || (0, env_1.isFirebaseMcp)()) && !!configstore_1.configstore.get("usage");
37
37
  }
38
38
  const GA4_USER_PROPS = {
39
39
  node_platform: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "firebase-tools",
3
- "version": "15.9.1",
3
+ "version": "15.10.0",
4
4
  "description": "Command-Line Interface for Firebase",
5
5
  "main": "./lib/index.js",
6
6
  "mcpName": "io.github.firebase/firebase-mcp",
@@ -10,6 +10,10 @@
10
10
  "type": "string",
11
11
  "description": "The name of the PostgreSQL database."
12
12
  },
13
+ "schema": {
14
+ "type": "string",
15
+ "description": "The PostgreSQL schema name. Defaults to 'public' if not specified."
16
+ },
13
17
  "cloudSql": {
14
18
  "additionalProperties": false,
15
19
  "type": "object",
@@ -9,6 +9,17 @@ App Name: ${appName}
9
9
 
10
10
  # Migration Guidelines
11
11
 
12
- - Focus on ensuring zero-friction deployments to Firebase App Hosting.
13
- - Maintain the original intent defined in docs/blueprint.md.
14
- - Use Genkit for AI features as already configured in src/ai/.
12
+ - `docs/blueprint.md` describes what this app was supposed to do when it was exported. Read it before making changes to the app.
13
+
14
+ - Use Agent Skills to guide Firebase usage. If they are not available, install them by running `npx -y skills add firebase/agent-skills --skill "*" -y`
15
+
16
+ - When asked to deploy or publish the app, deploy to Firebase App Hosting using `firebase deploy`.
17
+ If you run into issues, ensure `firebase.json` contains a valid `apphosting` block (with `backendId` and `rootDir`).
18
+ DO NOT use `firebase apphosting:backends:create` or `firebase apphosting:rollouts:create`.
19
+ DO NOT try to deploy to Firebase Hosting.
20
+
21
+ - When asked to add AI features, use Genkit (as already configured in `src/ai/`).
22
+
23
+ # Important
24
+
25
+ Never display, log, or commit sensitive credentials, .env files, or service account keys.
@@ -5,12 +5,8 @@ description: Run initial checks and fix common migration issues
5
5
 
6
6
  # Step 1: Check Compilation
7
7
 
8
- Run \`npm run typecheck\` and \`npm run build\` to ensure the project is in a healthy state.
8
+ Run \`npm run typecheck\` and \`npm run build\` (depending on the app type) to ensure the project is in a healthy state.
9
9
 
10
- # Step 2: Verify Firebase Auth/Firestore
10
+ # Step 2: Cleanup Genkit config
11
11
 
12
- If the app uses Firebase services, ensure the environment variables are correctly set or provided via App Hosting.
13
-
14
- # Step 3: Cleanup Genkit config
15
-
16
- If genkit is otherwise unused in this project, remove the configuration in src/ai/genkit.ts and remove related dependencies in package.json.
12
+ If genkit is otherwise unused in this project, ask the user if they'd like to remove the configuration in src/ai/genkit.ts and remove related dependencies in package.json.