Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
108 changes: 101 additions & 7 deletions npm-shrinkwrap.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@
"exit-code": "^1.0.2",
"express": "^4.16.4",
"filesize": "^6.1.0",
"firebase-frameworks": "^0.3.0",
"firebase-frameworks": "^0.4.0",
"fs-extra": "^5.0.0",
"glob": "^7.1.2",
"google-auth-library": "^7.11.0",
Expand Down
3 changes: 2 additions & 1 deletion src/deploy/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import * as FunctionsTarget from "./functions";
import * as StorageTarget from "./storage";
import * as RemoteConfigTarget from "./remoteconfig";
import * as ExtensionsTarget from "./extensions";
import { prepareFrameworks } from "../frameworks";

const TARGETS = {
hosting: HostingTarget,
Expand Down Expand Up @@ -58,7 +59,7 @@ export const deploy = async function (
if (previews.frameworkawareness && targetNames.includes("hosting")) {
const config = options.config.get("hosting");
if (Array.isArray(config) ? config.some((it) => it.source) : config.source) {
await require("firebase-frameworks").prepare(targetNames, context, options);
await prepareFrameworks(targetNames, context, options);
}
}

Expand Down
9 changes: 9 additions & 0 deletions src/emulator/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ import { ParsedTriggerDefinition } from "./functionsEmulatorShared";
import { ExtensionsEmulator } from "./extensionsEmulator";
import { normalizeAndValidate } from "../functions/projectConfig";
import { requiresJava } from "./downloadableEmulators";
import { prepareFrameworks } from "../frameworks";
import { previews } from "../previews";

const START_LOGGING_EMULATOR = utils.envOverride(
"START_LOGGING_EMULATOR",
Expand Down Expand Up @@ -402,6 +404,13 @@ export async function startAll(
}
}

if (previews.frameworkawareness) {
const config = options.config.get("hosting");
if (Array.isArray(config) ? config.some((it) => it.source) : config.source) {
await prepareFrameworks(targets, options, options);
}
}

if (shouldStart(options, Emulators.HUB)) {
const hubAddr = await getAndCheckAddress(Emulators.HUB, options);
const hub = new EmulatorHub({ projectId, ...hubAddr });
Expand Down
123 changes: 123 additions & 0 deletions src/frameworks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { join } from "path";
import { exit } from "process";

import { needProjectId } from "../projectUtils";
import { normalizedHostingConfigs } from "../hosting/normalizedHostingConfigs";
import { listSites, Site } from "../hosting/api";
import { getAppConfig, AppPlatform } from "../management/apps";
import { promises as fsPromises } from "fs";
import { promptOnce } from "../prompt";

const { writeFile } = fsPromises;

export const shortSiteName = (site?: Site) => site?.name && site.name.split("/").pop();

export const prepareFrameworks = async (targetNames: string[], context: any, options: any) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add docs on what this function's purpose is

const project = needProjectId(context);
// options.site is not present when emulated. We could call requireHostingSite but IAM permissions haven't
// been booted up (at this point) and we may be offline, so just use projectId. Most of the time
// the default site is named the same as the project & for frameworks this is only used for naming the
// function... unless you're using authenticated server-context TODO explore the implication here.
const configs = normalizedHostingConfigs({ site: project, ...options }, { resolveTargets: true });
options.normalizedHostingConfigs = configs;
if (configs.length === 0) return;
for (const config of configs) {
const { source, site, public: publicDir } = config;
if (!source) continue;
const dist = join(".firebase", site);
const hostingDist = join(dist, "hosting");
const functionsDist = join(dist, "functions");
if (publicDir)
throw new Error(`hosting.public and hosting.source cannot both be set in firebase.json`);
const getProjectPath = (...args: string[]) => join(process.cwd(), source, ...args);
const functionName = `ssr${site.replace(/-/g, "")}`;
const { build } = require("firebase-frameworks/tools");
const { usingCloudFunctions, rewrites, redirects, headers, usesFirebaseConfig } = await build(
{
dist,
project,
site,
function: {
name: functionName,
region: "us-central1",
},
},
getProjectPath
);
config.public = hostingDist;
if (usingCloudFunctions) {
if (context.hostingChannel) {
// TODO move to prompts
const message =
"Cannot preview changes to the backend, you will only see changes to the static content on this channel.";
if (!options.nonInteractive) {
const continueDeploy = await promptOnce({
type: "confirm",
default: true,
message: `${message} Would you like to continue with the deploy?`,
});
if (!continueDeploy) exit(1);
} else {
console.error(message);
}
} else {
const functionConfig = {
source: functionsDist,
codebase: `firebase-frameworks-${site}`,
};
if (targetNames.includes("functions")) {
const combinedFunctionsConfig = [functionConfig].concat(
options.config.get("functions") || []
);
options.config.set("functions", combinedFunctionsConfig);
} else {
targetNames.unshift("functions");
options.config.set("functions", functionConfig);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: add spaces and comments for this block of code. (L16-L77)

Reasoning -
A lot is going on here, its hard to understand the what/why of the code when its densely packed.


config.rewrites = [
...(config.rewrites || []),
...rewrites,
{
source: "**",
function: functionName,
},
];

let firebaseProjectConfig = null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: let firebaseProjectConfig;

(unless null string is intentional)
(JSON.stringify(null) => 'null')

if (usesFirebaseConfig) {
const sites = await listSites(project);
const selectedSite = sites.find((it) => shortSiteName(it) === site);
if (selectedSite) {
const { appId } = selectedSite;
if (appId) {
firebaseProjectConfig = await getAppConfig(appId, AppPlatform.WEB);
} else {
console.warn(
`No Firebase app associated with site ${site}, unable to provide authenticated server context`
);
}
}
}
writeFile(
join(functionsDist, ".env"),
`FRAMEWORKS_FIREBASE_PROJECT_CONFIG="${JSON.stringify(firebaseProjectConfig).replace(
/"/g,
'\\"'
)}"`
);
} else {
config.rewrites = [
...(config.rewrites || []),
...rewrites,
{
source: "**",
destination: "/index.html",
},
];
}
config.redirects = [...(config.redirects || []), ...redirects];
config.headers = [...(config.headers || []), ...headers];
}
};
8 changes: 8 additions & 0 deletions src/hosting/normalizedHostingConfigs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,13 @@ import { cloneDeep } from "lodash";
import { FirebaseError } from "../error";

interface HostingConfig {
source?: string;
public?: string;
site: string;
target: string;
rewrites?: any[];
redirects?: any[];
headers?: any[];
}

function filterOnly(configs: HostingConfig[], onlyString: string): HostingConfig[] {
Expand Down Expand Up @@ -90,6 +95,9 @@ export function normalizedHostingConfigs(
cmdOptions: any, // eslint-disable-line @typescript-eslint/no-explicit-any
options: { resolveTargets?: boolean } = {}
): HostingConfig[] {
// First see if there's a momoized copy on the options, from frameworks
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: memoized

const normalizedHostingConfigs = cmdOptions.normalizedHostingConfigs;
if (normalizedHostingConfigs) return normalizedHostingConfigs;
let configs = cloneDeep(cmdOptions.config.get("hosting"));
if (!configs) {
return [];
Expand Down
7 changes: 2 additions & 5 deletions src/serve/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { EmulatorServer } from "../emulator/emulatorServer";
import * as _ from "lodash";
import { logger } from "../logger";
import { prepareFrameworks } from "../frameworks";
import { previews } from "../previews";

const { FunctionsServer } = require("./functions");
Expand All @@ -26,11 +27,7 @@ export async function serve(options: any): Promise<void> {
targetNames.includes("hosting") &&
[].concat(options.config.get("hosting")).some((it: any) => it.source)
) {
await require("firebase-frameworks").prepare(
targetNames,
{ project: options.projectId },
options
);
await prepareFrameworks(targetNames, options, options);
}
await Promise.all(
_.map(targetNames, (targetName: string) => {
Expand Down