Skip to content
Open
21 changes: 12 additions & 9 deletions src/commands/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
import { errNoDefaultSite } from "../getDefaultHostingSite";
import { FirebaseError } from "../error";
import { bold } from "colorette";
import { interactiveCreateHostingSite } from "../hosting/interactive";
import { pickHostingSiteName } from "../hosting/interactive";
import { logBullet } from "../utils";
import { createSite } from "../hosting/api";
import { Options } from "../options";

// in order of least time-consuming to most time-consuming
export const VALID_DEPLOY_TARGETS = [
Expand Down Expand Up @@ -98,26 +100,26 @@
"In order to provide better validation, this may still enable APIs on the target project",
)
.before(requireConfig)
.before((options) => {
.before((options: Options) => {
options.filteredTargets = filterTargets(options, VALID_DEPLOY_TARGETS);
const permissions = options.filteredTargets.reduce((perms: string[], target: string) => {
return perms.concat(TARGET_PERMISSIONS[target]);
}, []);
return requirePermissions(options, permissions);
})
.before((options) => {
.before((options: Options) => {
if (options.filteredTargets.includes("functions")) {
return checkServiceAccountIam(options.project);
return checkServiceAccountIam(options.project!);

Check warning on line 112 in src/commands/deploy.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Forbidden non-null assertion
}
})
.before(async (options) => {
.before(async (options: Options) => {
// only fetch the default instance for hosting or database deploys
if (options.filteredTargets.includes("database")) {
await requireDatabaseInstance(options);
}

if (options.filteredTargets.includes("hosting")) {
let createSite = false;
let shouldCreateSite = false;
try {
await requireHostingSite(options);
} catch (err: unknown) {
Expand All @@ -128,10 +130,10 @@
if (isPermissionError) {
throw err;
} else if (err === errNoDefaultSite) {
createSite = true;
shouldCreateSite = true;
}
}
if (!createSite) {
if (!shouldCreateSite) {
return;
}
if (options.nonInteractive) {
Expand All @@ -142,10 +144,11 @@
);
}
logBullet("No Hosting site detected.");
await interactiveCreateHostingSite("", "", options);
const siteId = await pickHostingSiteName("", options);
await createSite(options.project!, siteId);

Check warning on line 148 in src/commands/deploy.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Forbidden non-null assertion
}
})
.before(checkValidTargetFilters)
.action((options) => {
return deploy(options.filteredTargets, options);

Check warning on line 153 in src/commands/deploy.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe argument of type `any` assigned to a parameter of type `DeployOptions`

Check warning on line 153 in src/commands/deploy.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe member access .filteredTargets on an `any` value

Check warning on line 153 in src/commands/deploy.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe argument of type `any` assigned to a parameter of type `("database" | "storage" | "firestore" | "functions" | "hosting" | "remoteconfig" | "extensions" | "dataconnect" | "apphosting")[]`
});
14 changes: 7 additions & 7 deletions src/commands/hosting-sites-create.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { bold } from "colorette";

import { Command } from "../command";
import { interactiveCreateHostingSite } from "../hosting/interactive";
import { last, logLabeledSuccess } from "../utils";
import { pickHostingSiteName } from "../hosting/interactive";
import { logLabeledSuccess } from "../utils";
import { logger } from "../logger";
import { needProjectId } from "../projectUtils";
import { Options } from "../options";
import { requirePermissions } from "../requirePermissions";
import { Site } from "../hosting/api";
import { createSite, Site } from "../hosting/api";
import { FirebaseError } from "../error";

const LOG_TAG = "hosting:sites";
Expand All @@ -16,16 +16,16 @@ export const command = new Command("hosting:sites:create [siteId]")
.description("create a Firebase Hosting site")
.option("--app <appId>", "specify an existing Firebase Web App ID")
.before(requirePermissions, ["firebasehosting.sites.update"])
.action(async (siteId: string, options: Options & { app: string }): Promise<Site> => {
.action(async (siteId: string | undefined, options: Options & { app: string }): Promise<Site> => {
const projectId = needProjectId(options);
const appId = options.app;

if (options.nonInteractive && !siteId) {
throw new FirebaseError(`${bold(siteId)} is required in a non-interactive environment`);
throw new FirebaseError(`${bold("siteId")} is required in a non-interactive environment`);
}

const site = await interactiveCreateHostingSite(siteId, appId, options);
siteId = last(site.name.split("/"));
siteId = await pickHostingSiteName(siteId ?? "", options);
const site = await createSite(projectId, siteId, appId);

logger.info();
logLabeledSuccess(
Expand Down
2 changes: 1 addition & 1 deletion src/frameworks/angular/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,16 @@

export const supportedRange = "16 - 20";

export async function discover(dir: string): Promise<Discovery | undefined> {

Check warning on line 41 in src/frameworks/angular/index.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Missing JSDoc comment
if (!(await pathExists(join(dir, "package.json")))) return;
if (!(await pathExists(join(dir, "angular.json")))) return;
const version = getAngularVersion(dir);
return { mayWantBackend: true, version };
}

export function init(setup: any, config: any) {

Check warning on line 48 in src/frameworks/angular/index.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unexpected any. Specify a different type

Check warning on line 48 in src/frameworks/angular/index.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unexpected any. Specify a different type

Check warning on line 48 in src/frameworks/angular/index.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Missing JSDoc comment

Check warning on line 48 in src/frameworks/angular/index.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Missing return type on function
execSync(
`npx --yes -p @angular/cli@"${supportedRange}" ng new ${setup.projectId} --directory ${setup.hosting.source} --skip-git`,
`npx --yes -p @angular/cli@"${supportedRange}" ng new ${setup.projectId} --directory ${setup.featureInfo.hosting.source} --skip-git`,
{
stdio: "inherit",
cwd: config.projectDir,
Expand Down
2 changes: 1 addition & 1 deletion src/frameworks/flutter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export function init(setup: any, config: any) {
`--project-name=${projectName}`,
"--overwrite",
"--platforms=web",
setup.hosting.source,
setup.featureInfo.hosting.source,
],
{ stdio: "inherit", cwd: config.projectDir },
);
Expand Down
2 changes: 1 addition & 1 deletion src/frameworks/next/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ export async function init(setup: any, config: any) {
});
execSync(
`npx --yes create-next-app@"${supportedRange}" -e hello-world ` +
`${setup.hosting.source} --use-npm --${language}`,
`${setup.featureInfo.hosting.source} --use-npm --${language}`,
{ stdio: "inherit", cwd: config.projectDir },
);
}
Expand Down
2 changes: 1 addition & 1 deletion src/frameworks/nuxt/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ export async function getConfig(cwd: string): Promise<NuxtOptions> {
* Utility method used during project initialization.
*/
export function init(setup: any, config: any) {
execSync(`npx --yes nuxi@"${supportedRange}" init ${setup.hosting.source}`, {
execSync(`npx --yes nuxi@"${supportedRange}" init ${setup.featureInfo.hosting.source}`, {
stdio: "inherit",
cwd: config.projectDir,
});
Expand Down
7 changes: 5 additions & 2 deletions src/frameworks/vite/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,16 @@ export async function init(setup: any, config: any, baseTemplate: string = "vani
],
});
execSync(
`npm create vite@"${supportedRange}" ${setup.hosting.source} --yes -- --template ${template}`,
`npm create vite@"${supportedRange}" ${setup.featureInfo.hosting.source} --yes -- --template ${template}`,
{
stdio: "inherit",
cwd: config.projectDir,
},
);
execSync(`npm install`, { stdio: "inherit", cwd: join(config.projectDir, setup.hosting.source) });
execSync(`npm install`, {
stdio: "inherit",
cwd: join(config.projectDir, setup.featureInfo.hosting.source),
});
}

export const viteDiscoverWithNpmDependency = (dep: string) => async (dir: string) =>
Expand Down
36 changes: 15 additions & 21 deletions src/hosting/interactive.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { FirebaseError } from "../error";
import { logWarning } from "../utils";
import { needProjectId, needProjectNumber } from "../projectUtils";
import { Site, createSite } from "./api";
import { createSite } from "./api";
import { input } from "../prompt";

const nameSuggestion = new RegExp("try something like `(.+)`");
Expand All @@ -11,17 +11,16 @@ const prompt =
'We recommend using letters, numbers, and hyphens (e.g. "{project-id}-{random-hash}"):';

/**
* Interactively prompt to create a Hosting site.
* Interactively prompt to name a Hosting site.
*/
export async function interactiveCreateHostingSite(
export async function pickHostingSiteName(
siteId: string,
appId: string,
options: { projectId?: string; nonInteractive?: boolean },
): Promise<Site> {
): Promise<string> {
const projectId = needProjectId(options);
const projectNumber = await needProjectNumber(options);
let id = siteId;
let newSite: Site | undefined;
let nameConfirmed: boolean = false;
let suggestion: string | undefined;

// If we were given an ID, we're going to start with that, so don't check the project ID.
Expand All @@ -35,34 +34,26 @@ export async function interactiveCreateHostingSite(
}
}

while (!newSite) {
while (!nameConfirmed) {
if (!id || suggestion) {
id = await input({
message: prompt,
validate: (s: string) => s.length > 0, // Prevents an empty string from being submitted!
default: suggestion,
});
}
try {
newSite = await createSite(projectNumber, id, appId);
} catch (err: unknown) {
if (!(err instanceof FirebaseError)) {
throw err;
}
if (options.nonInteractive) {
throw err;
}

id = ""; // Clear so the prompt comes back.
suggestion = getSuggestionFromError(err);
}
const attempt = await trySiteID(projectNumber, id);
nameConfirmed = attempt.available;
suggestion = attempt.suggestion;
if (!nameConfirmed) id = ""; // Clear so the prompt comes back.
}
return newSite;
return id;
}

async function trySiteID(
projectNumber: string,
id: string,
nonInteractive = false,
): Promise<{ available: boolean; suggestion?: string }> {
try {
await createSite(projectNumber, id, "", true);
Expand All @@ -71,6 +62,9 @@ async function trySiteID(
if (!(err instanceof FirebaseError)) {
throw err;
}
if (nonInteractive) {
throw err;
}
const suggestion = getSuggestionFromError(err);
return { available: false, suggestion };
}
Expand Down
Loading
Loading