123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417 |
- "use strict";
- Object.defineProperty(exports, "__esModule", { value: true });
- exports.createServerResponseProxy = exports.prepareFrameworks = exports.findDependency = exports.discover = exports.relativeRequire = exports.WebFrameworks = exports.NODE_VERSION = exports.DEFAULT_REGION = exports.FIREBASE_ADMIN_VERSION = exports.FIREBASE_FUNCTIONS_VERSION = exports.FIREBASE_FRAMEWORKS_VERSION = void 0;
- const path_1 = require("path");
- const process_1 = require("process");
- const child_process_1 = require("child_process");
- const fs_1 = require("fs");
- const url_1 = require("url");
- const http_1 = require("http");
- const promises_1 = require("fs/promises");
- const fs_extra_1 = require("fs-extra");
- const clc = require("colorette");
- const process = require("node:process");
- const semver = require("semver");
- const projectUtils_1 = require("../projectUtils");
- const config_1 = require("../hosting/config");
- const api_1 = require("../hosting/api");
- const apps_1 = require("../management/apps");
- const prompt_1 = require("../prompt");
- const types_1 = require("../emulator/types");
- const defaultCredentials_1 = require("../defaultCredentials");
- const auth_1 = require("../auth");
- const functionsEmulatorShared_1 = require("../emulator/functionsEmulatorShared");
- const constants_1 = require("../emulator/constants");
- const error_1 = require("../error");
- const requireHostingSite_1 = require("../requireHostingSite");
- const experiments = require("../experiments");
- const ensureTargeted_1 = require("../functions/ensureTargeted");
- const implicitInit_1 = require("../hosting/implicitInit");
- const { dynamicImport } = require(true && "../dynamicImport");
- const SupportLevelWarnings = {
- ["experimental"]: clc.yellow(`This is an experimental integration, proceed with caution.`),
- ["community-supported"]: clc.yellow(`This is a community-supported integration, support is best effort.`),
- };
- exports.FIREBASE_FRAMEWORKS_VERSION = "^0.6.0";
- exports.FIREBASE_FUNCTIONS_VERSION = "^3.23.0";
- exports.FIREBASE_ADMIN_VERSION = "^11.0.1";
- exports.DEFAULT_REGION = "us-central1";
- exports.NODE_VERSION = parseInt(process.versions.node, 10).toString();
- const DEFAULT_FIND_DEP_OPTIONS = {
- cwd: process.cwd(),
- omitDev: true,
- };
- const NPM_COMMAND = process.platform === "win32" ? "npm.cmd" : "npm";
- exports.WebFrameworks = Object.fromEntries((0, fs_1.readdirSync)(__dirname)
- .filter((path) => (0, fs_1.statSync)((0, path_1.join)(__dirname, path)).isDirectory())
- .map((path) => [path, require((0, path_1.join)(__dirname, path))])
- .filter(([, obj]) => obj.name && obj.discover && obj.build && obj.type !== undefined && obj.support));
- function relativeRequire(dir, mod) {
- try {
- const path = require.resolve(mod, { paths: [dir] });
- if ((0, path_1.extname)(path) === ".mjs") {
- return dynamicImport((0, url_1.pathToFileURL)(path).toString());
- }
- else {
- return require(path);
- }
- }
- catch (e) {
- const path = (0, path_1.relative)(process.cwd(), dir);
- console.error(`Could not load dependency ${mod} in ${path.startsWith("..") ? path : `./${path}`}, have you run \`npm install\`?`);
- throw e;
- }
- }
- exports.relativeRequire = relativeRequire;
- async function discover(dir, warn = true) {
- const allFrameworkTypes = [
- ...new Set(Object.values(exports.WebFrameworks).map(({ type }) => type)),
- ].sort();
- for (const discoveryType of allFrameworkTypes) {
- const frameworksDiscovered = [];
- for (const framework in exports.WebFrameworks) {
- if (exports.WebFrameworks[framework]) {
- const { discover, type } = exports.WebFrameworks[framework];
- if (type !== discoveryType)
- continue;
- const result = await discover(dir);
- if (result)
- frameworksDiscovered.push(Object.assign({ framework }, result));
- }
- }
- if (frameworksDiscovered.length > 1) {
- if (warn)
- console.error("Multiple conflicting frameworks discovered.");
- return;
- }
- if (frameworksDiscovered.length === 1)
- return frameworksDiscovered[0];
- }
- if (warn)
- console.warn("Could not determine the web framework in use.");
- return;
- }
- exports.discover = discover;
- function scanDependencyTree(searchingFor, dependencies = {}) {
- for (const [name, dependency] of Object.entries(dependencies)) {
- if (name === searchingFor)
- return dependency;
- const result = scanDependencyTree(searchingFor, dependency.dependencies);
- if (result)
- return result;
- }
- return;
- }
- function findDependency(name, options = {}) {
- const { cwd, depth, omitDev } = Object.assign(Object.assign({}, DEFAULT_FIND_DEP_OPTIONS), options);
- const env = Object.assign({}, process.env);
- delete env.NODE_ENV;
- const result = (0, child_process_1.spawnSync)(NPM_COMMAND, [
- "list",
- name,
- "--json",
- ...(omitDev ? ["--omit", "dev"] : []),
- ...(depth === undefined ? [] : ["--depth", depth.toString(10)]),
- ], { cwd, env });
- if (!result.stdout)
- return;
- const json = JSON.parse(result.stdout.toString());
- return scanDependencyTree(name, json.dependencies);
- }
- exports.findDependency = findDependency;
- async function prepareFrameworks(targetNames, context, options, emulators = []) {
- var _a;
- var _b, _c, _d, _e;
- const nodeVersion = process.version;
- if (!semver.satisfies(nodeVersion, ">=16.0.0")) {
- throw new error_1.FirebaseError(`The frameworks awareness feature requires Node.JS >= 16 and npm >= 8 in order to work correctly, due to some of the downstream dependencies. Please upgrade your version of Node.JS, reinstall firebase-tools, and give it another go.`);
- }
- const project = (0, projectUtils_1.needProjectId)(context);
- const { projectRoot } = options;
- const account = (0, auth_1.getProjectDefaultAccount)(projectRoot);
- if (!options.site) {
- try {
- await (0, requireHostingSite_1.requireHostingSite)(options);
- }
- catch (_f) {
- options.site = project;
- }
- }
- const configs = (0, config_1.hostingConfig)(options);
- let firebaseDefaults = undefined;
- if (configs.length === 0) {
- return;
- }
- for (const config of configs) {
- const { source, site, public: publicDir } = config;
- if (!source) {
- continue;
- }
- config.rewrites || (config.rewrites = []);
- config.redirects || (config.redirects = []);
- config.headers || (config.headers = []);
- (_a = config.cleanUrls) !== null && _a !== void 0 ? _a : (config.cleanUrls = true);
- const dist = (0, path_1.join)(projectRoot, ".firebase", site);
- const hostingDist = (0, path_1.join)(dist, "hosting");
- const functionsDist = (0, path_1.join)(dist, "functions");
- if (publicDir) {
- throw new Error(`hosting.public and hosting.source cannot both be set in firebase.json`);
- }
- const getProjectPath = (...args) => (0, path_1.join)(projectRoot, source, ...args);
- const functionName = `ssr${site.toLowerCase().replace(/-/g, "")}`;
- const usesFirebaseAdminSdk = !!findDependency("firebase-admin", { cwd: getProjectPath() });
- const usesFirebaseJsSdk = !!findDependency("@firebase/app", { cwd: getProjectPath() });
- if (usesFirebaseAdminSdk) {
- process.env.GOOGLE_CLOUD_PROJECT = project;
- if (account && !process.env.GOOGLE_APPLICATION_CREDENTIALS) {
- const defaultCredPath = await (0, defaultCredentials_1.getCredentialPathAsync)(account);
- if (defaultCredPath)
- process.env.GOOGLE_APPLICATION_CREDENTIALS = defaultCredPath;
- }
- }
- emulators.forEach((info) => {
- if (usesFirebaseAdminSdk) {
- if (info.name === types_1.Emulators.FIRESTORE)
- process.env[constants_1.Constants.FIRESTORE_EMULATOR_HOST] = (0, functionsEmulatorShared_1.formatHost)(info);
- if (info.name === types_1.Emulators.AUTH)
- process.env[constants_1.Constants.FIREBASE_AUTH_EMULATOR_HOST] = (0, functionsEmulatorShared_1.formatHost)(info);
- if (info.name === types_1.Emulators.DATABASE)
- process.env[constants_1.Constants.FIREBASE_DATABASE_EMULATOR_HOST] = (0, functionsEmulatorShared_1.formatHost)(info);
- if (info.name === types_1.Emulators.STORAGE)
- process.env[constants_1.Constants.FIREBASE_STORAGE_EMULATOR_HOST] = (0, functionsEmulatorShared_1.formatHost)(info);
- }
- if (usesFirebaseJsSdk && types_1.EMULATORS_SUPPORTED_BY_USE_EMULATOR.includes(info.name)) {
- firebaseDefaults || (firebaseDefaults = {});
- firebaseDefaults.emulatorHosts || (firebaseDefaults.emulatorHosts = {});
- firebaseDefaults.emulatorHosts[info.name] = (0, functionsEmulatorShared_1.formatHost)(info);
- }
- });
- let firebaseConfig = null;
- if (usesFirebaseJsSdk) {
- const sites = await (0, api_1.listSites)(project);
- const selectedSite = sites.find((it) => it.name && it.name.split("/").pop() === site);
- if (selectedSite) {
- const { appId } = selectedSite;
- if (appId) {
- firebaseConfig = await (0, apps_1.getAppConfig)(appId, apps_1.AppPlatform.WEB);
- firebaseDefaults || (firebaseDefaults = {});
- firebaseDefaults.config = firebaseConfig;
- }
- else {
- const defaultConfig = await (0, implicitInit_1.implicitInit)(options);
- if (defaultConfig.json) {
- console.warn(`No Firebase app associated with site ${site}, injecting project default config.
- You can link a Web app to a Hosting site here https://console.firebase.google.com/project/${project}/settings/general/web`);
- firebaseDefaults || (firebaseDefaults = {});
- firebaseDefaults.config = JSON.parse(defaultConfig.json);
- }
- else {
- console.warn(`No Firebase app associated with site ${site}, unable to provide authenticated server context.
- You can link a Web app to a Hosting site here https://console.firebase.google.com/project/${project}/settings/general/web`);
- if (!options.nonInteractive) {
- const continueDeploy = await (0, prompt_1.promptOnce)({
- type: "confirm",
- default: true,
- message: "Would you like to continue with the deploy?",
- });
- if (!continueDeploy)
- (0, process_1.exit)(1);
- }
- }
- }
- }
- }
- if (firebaseDefaults)
- process.env.__FIREBASE_DEFAULTS__ = JSON.stringify(firebaseDefaults);
- const results = await discover(getProjectPath());
- if (!results)
- throw new Error("Epic fail.");
- const { framework, mayWantBackend, publicDirectory } = results;
- const { build, ɵcodegenPublicDirectory, ɵcodegenFunctionsDirectory: codegenProdModeFunctionsDirectory, getDevModeHandle, name, support, } = exports.WebFrameworks[framework];
- console.log(`Detected a ${name} codebase. ${SupportLevelWarnings[support] || ""}\n`);
- const isDevMode = context._name === "serve" || context._name === "emulators:start";
- const hostingEmulatorInfo = emulators.find((e) => e.name === types_1.Emulators.HOSTING);
- const devModeHandle = isDevMode &&
- getDevModeHandle &&
- (await getDevModeHandle(getProjectPath(), hostingEmulatorInfo));
- let codegenFunctionsDirectory;
- if (devModeHandle) {
- config.public = (0, path_1.relative)(projectRoot, publicDirectory);
- options.frameworksDevModeHandle = devModeHandle;
- if (mayWantBackend && firebaseDefaults) {
- codegenFunctionsDirectory = codegenDevModeFunctionsDirectory;
- }
- }
- else {
- const { wantsBackend = false, rewrites = [], redirects = [], headers = [], } = (await build(getProjectPath())) || {};
- config.rewrites.push(...rewrites);
- config.redirects.push(...redirects);
- config.headers.push(...headers);
- if (await (0, fs_extra_1.pathExists)(hostingDist))
- await (0, promises_1.rm)(hostingDist, { recursive: true });
- await (0, fs_extra_1.mkdirp)(hostingDist);
- await ɵcodegenPublicDirectory(getProjectPath(), hostingDist);
- config.public = (0, path_1.relative)(projectRoot, hostingDist);
- if (wantsBackend)
- codegenFunctionsDirectory = codegenProdModeFunctionsDirectory;
- }
- config.webFramework = `${framework}${codegenFunctionsDirectory ? "_ssr" : ""}`;
- if (codegenFunctionsDirectory) {
- if (firebaseDefaults)
- firebaseDefaults._authTokenSyncURL = "/__session";
- const rewrite = {
- source: "**",
- function: {
- functionId: functionName,
- },
- };
- if (experiments.isEnabled("pintags")) {
- rewrite.function.pinTag = true;
- }
- config.rewrites.push(rewrite);
- const codebase = `firebase-frameworks-${site}`;
- const existingFunctionsConfig = options.config.get("functions")
- ? [].concat(options.config.get("functions"))
- : [];
- options.config.set("functions", [
- ...existingFunctionsConfig,
- {
- source: (0, path_1.relative)(projectRoot, functionsDist),
- codebase,
- },
- ]);
- if (!targetNames.includes("functions")) {
- targetNames.unshift("functions");
- }
- if (options.only) {
- options.only = (0, ensureTargeted_1.ensureTargeted)(options.only, codebase);
- }
- if (await (0, fs_extra_1.pathExists)(functionsDist)) {
- const functionsDistStat = await (0, fs_extra_1.stat)(functionsDist);
- if (functionsDistStat === null || functionsDistStat === void 0 ? void 0 : functionsDistStat.isDirectory()) {
- const files = await (0, promises_1.readdir)(functionsDist);
- for (const file of files) {
- if (file !== "node_modules" && file !== "package-lock.json")
- await (0, promises_1.rm)((0, path_1.join)(functionsDist, file), { recursive: true });
- }
- }
- else {
- await (0, promises_1.rm)(functionsDist);
- }
- }
- else {
- await (0, fs_extra_1.mkdirp)(functionsDist);
- }
- const { packageJson, bootstrapScript, frameworksEntry = framework, } = await codegenFunctionsDirectory(getProjectPath(), functionsDist);
- await (0, promises_1.writeFile)((0, path_1.join)(functionsDist, "functions.yaml"), JSON.stringify({
- endpoints: {
- [functionName]: {
- platform: "gcfv2",
- region: [exports.DEFAULT_REGION],
- labels: {},
- httpsTrigger: {},
- entryPoint: "ssr",
- },
- },
- specVersion: "v1alpha1",
- requiredAPIs: [],
- }, null, 2));
- packageJson.main = "server.js";
- delete packageJson.devDependencies;
- packageJson.dependencies || (packageJson.dependencies = {});
- (_b = packageJson.dependencies)["firebase-frameworks"] || (_b["firebase-frameworks"] = exports.FIREBASE_FRAMEWORKS_VERSION);
- (_c = packageJson.dependencies)["firebase-functions"] || (_c["firebase-functions"] = exports.FIREBASE_FUNCTIONS_VERSION);
- (_d = packageJson.dependencies)["firebase-admin"] || (_d["firebase-admin"] = exports.FIREBASE_ADMIN_VERSION);
- packageJson.engines || (packageJson.engines = {});
- (_e = packageJson.engines).node || (_e.node = exports.NODE_VERSION);
- await (0, promises_1.writeFile)((0, path_1.join)(functionsDist, "package.json"), JSON.stringify(packageJson, null, 2));
- await (0, promises_1.writeFile)((0, path_1.join)(functionsDist, ".env"), `__FIREBASE_FRAMEWORKS_ENTRY__=${frameworksEntry}
- ${firebaseDefaults ? `__FIREBASE_DEFAULTS__=${JSON.stringify(firebaseDefaults)}\n` : ""}`);
- await (0, promises_1.copyFile)(getProjectPath("package-lock.json"), (0, path_1.join)(functionsDist, "package-lock.json")).catch(() => {
- });
- if (await (0, fs_extra_1.pathExists)(getProjectPath(".npmrc"))) {
- await (0, promises_1.copyFile)(getProjectPath(".npmrc"), (0, path_1.join)(functionsDist, ".npmrc"));
- }
- (0, child_process_1.execSync)(`${NPM_COMMAND} i --omit dev --no-audit`, {
- cwd: functionsDist,
- stdio: "inherit",
- });
- if (bootstrapScript)
- await (0, promises_1.writeFile)((0, path_1.join)(functionsDist, "bootstrap.js"), bootstrapScript);
- await (0, promises_1.writeFile)((0, path_1.join)(functionsDist, "server.js"), `const { onRequest } = require('firebase-functions/v2/https');
- const server = import('firebase-frameworks');
- exports.ssr = onRequest((req, res) => server.then(it => it.handle(req, res)));
- `);
- }
- else {
- config.rewrites.push({
- source: "**",
- destination: "/index.html",
- });
- }
- if (firebaseDefaults) {
- const encodedDefaults = Buffer.from(JSON.stringify(firebaseDefaults)).toString("base64url");
- const expires = new Date(new Date().getTime() + 60000000000);
- const sameSite = "Strict";
- const path = `/`;
- config.headers.push({
- source: "**/*.js",
- headers: [
- {
- key: "Set-Cookie",
- value: `__FIREBASE_DEFAULTS__=${encodedDefaults}; SameSite=${sameSite}; Expires=${expires.toISOString()}; Path=${path};`,
- },
- ],
- });
- }
- }
- }
- exports.prepareFrameworks = prepareFrameworks;
- function codegenDevModeFunctionsDirectory() {
- const packageJson = {};
- return Promise.resolve({ packageJson, frameworksEntry: "_devMode" });
- }
- function createServerResponseProxy(req, res, next) {
- const proxiedRes = new http_1.ServerResponse(req);
- const buffer = [];
- proxiedRes.write = new Proxy(proxiedRes.write.bind(proxiedRes), {
- apply: (target, thisArg, args) => {
- target.call(thisArg, ...args);
- buffer.push(["write", args]);
- },
- });
- proxiedRes.setHeader = new Proxy(proxiedRes.setHeader.bind(proxiedRes), {
- apply: (target, thisArg, args) => {
- target.call(thisArg, ...args);
- buffer.push(["setHeader", args]);
- },
- });
- proxiedRes.removeHeader = new Proxy(proxiedRes.removeHeader.bind(proxiedRes), {
- apply: (target, thisArg, args) => {
- target.call(thisArg, ...args);
- buffer.push(["removeHeader", args]);
- },
- });
- proxiedRes.writeHead = new Proxy(proxiedRes.writeHead.bind(proxiedRes), {
- apply: (target, thisArg, args) => {
- target.call(thisArg, ...args);
- buffer.push(["writeHead", args]);
- },
- });
- proxiedRes.end = new Proxy(proxiedRes.end.bind(proxiedRes), {
- apply: (target, thisArg, args) => {
- target.call(thisArg, ...args);
- if (proxiedRes.statusCode === 404) {
- next();
- }
- else {
- for (const [fn, args] of buffer) {
- res[fn](...args);
- }
- res.end(...args);
- }
- },
- });
- return proxiedRes;
- }
- exports.createServerResponseProxy = createServerResponseProxy;
|