Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
0722a04
CTA
jamesdaniels Apr 11, 2023
75d66d0
Merge branch 'master' into frameworksCTA
jamesdaniels Apr 14, 2023
c7a2f12
Merge branch 'master' into frameworksCTA
jamesdaniels Apr 14, 2023
93a871e
Formatting, handle Next 13.3 app dir
jamesdaniels Apr 14, 2023
eaaa762
Better reasons for backend
jamesdaniels Apr 14, 2023
15a4806
Do headers right
jamesdaniels Apr 14, 2023
af9d46a
Detect image optimization in app directory
jamesdaniels Apr 14, 2023
940f22c
Trip on non-rendered rsc
jamesdaniels Apr 14, 2023
e845f8c
Copy the pages manifest files
jamesdaniels Apr 14, 2023
74281f0
Add a timeout while bundling next.config.js
jamesdaniels Apr 19, 2023
dc35e5e
Merge branch 'master' into frameworksCTA
jamesdaniels Apr 26, 2023
bea352b
More
jamesdaniels Apr 28, 2023
23fd64a
Merged master
jamesdaniels Apr 28, 2023
e5daf35
Dont need the close it seems
jamesdaniels Apr 28, 2023
5fb9380
Lint
jamesdaniels Apr 28, 2023
26e4f82
CTA cleanup
jamesdaniels Apr 28, 2023
3ab19ce
Lint
jamesdaniels Apr 28, 2023
acc98b3
Bump
jamesdaniels Apr 28, 2023
7bdb21a
Bump
jamesdaniels Apr 28, 2023
578b3cb
get non static routes as util + unit test
leoortizz Apr 30, 2023
e60740c
get non static components util + unit test
leoortizz Apr 30, 2023
22a51c7
get headers from .meta files util + unit test
leoortizz Apr 30, 2023
056b6b2
more types, NextJS > Next.js
leoortizz Apr 30, 2023
14a6006
Next.js integration test (#5768)
leoortizz May 1, 2023
79641b6
Merge branch 'master' into frameworksCTA
leoortizz May 2, 2023
65a4ed8
fix Flutter imports
leoortizz May 2, 2023
c024f5c
Merge branch 'master' into frameworksCTA
jamesdaniels May 2, 2023
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
1 change: 1 addition & 0 deletions .github/workflows/node-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ jobs:
- npm run test:storage-emulator-integration
- npm run test:triggers-end-to-end
- npm run test:triggers-end-to-end:inspect
- npm run test:webframeworks-deploy
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export async function GET() {
return new Response(JSON.stringify([1, 2, 3]), {
status: 200,
headers: {
"content-type": "application/json",
"custom-header": "custom-value",
},
});
}
2 changes: 2 additions & 0 deletions scripts/webframeworks-deploy-tests/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ set -e # Immediately exit on failure
# Globally link the CLI for the testing framework
./scripts/clean-install.sh

source scripts/set-default-credentials.sh

(cd scripts/webframeworks-deploy-tests/hosting; npm i; npm run build)

mocha scripts/webframeworks-deploy-tests/tests.ts
95 changes: 85 additions & 10 deletions scripts/webframeworks-deploy-tests/tests.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { expect } from "chai";
import * as glob from "glob";

import * as cli from "./cli";
import { requireAuth } from "../../src/requireAuth";
import { getBuildId } from "../../src/frameworks/next/utils";
import { relative } from "path";

const FIREBASE_PROJECT = process.env.GCLOUD_PROJECT || "";
const FIREBASE_PROJECT = process.env.FBTOOLS_TARGET_PROJECT || "";
const FIREBASE_DEBUG = process.env.FIREBASE_DEBUG || "";

function genRandomId(n = 10): string {
Expand All @@ -15,37 +18,109 @@ function genRandomId(n = 10): string {
return id;
}

describe("webframeworks deploy", function (this) {
async function getFilesListFromDir(dir: string): Promise<string[]> {
const files = await new Promise<string[]>((resolve, reject) => {
glob(`${dir}/**/*`, (err, matches) => {
if (err) reject(err);
resolve(matches);
});
});
return files.map((path) => relative(dir, path));
}

describe("webframeworks deploy build", function (this) {
this.timeout(1000_000);

let result: cli.Result;

const RUN_ID = genRandomId();
console.log(`TEST RUN: ${RUN_ID}`);

async function setOptsAndDeploy(): Promise<cli.Result> {
const args = [];
async function setOptsAndBuild(): Promise<cli.Result> {
const args = ["exit 0"];
if (FIREBASE_DEBUG) {
args.push("--debug");
}
return await cli.exec("deploy", FIREBASE_PROJECT, args, __dirname, false);

return await cli.exec("emulators:exec", FIREBASE_PROJECT, args, __dirname, false);
}

before(async () => {
expect(FIREBASE_PROJECT).to.not.be.empty;

await requireAuth({});
process.env.FIREBASE_CLI_EXPERIMENTS = "webframeworks";
result = await setOptsAndBuild();
});

after(() => {
// This is not an empty block.
});

it("deploys functions with runtime options", async () => {
it("should log reasons for backend", () => {
process.env.FIREBASE_CLI_EXPERIMENTS = "webframeworks";

const result = await setOptsAndDeploy();
expect(result.stdout, "build result").to.match(
/Building a Cloud Function to run this application. This is needed due to:/
);
expect(result.stdout, "build result").to.match(/middleware/);
expect(result.stdout, "build result").to.match(/Image Optimization/);
expect(result.stdout, "build result").to.match(/use of revalidate \/bar/);
expect(result.stdout, "build result").to.match(/non-static route \/api\/hello/);
});

it("should have the expected static files to be deployed", async () => {
const buildId = await getBuildId(`${__dirname}/hosting/.next`);

const DOT_FIREBASE_FOLDER_PATH = `${__dirname}/.firebase/${FIREBASE_PROJECT}`;

const EXPECTED_FILES = [
`_next`,
`_next/static`,
`_next/static/chunks`,
`_next/static/chunks/app`,
`_next/static/chunks/app/bar`,
`_next/static/chunks/app/foo`,
`_next/static/chunks/pages`,
`_next/static/chunks/pages/about`,
`_next/static/css`,
`_next/static/${buildId}`,
`_next/static/${buildId}/_buildManifest.js`,
`_next/static/${buildId}/_ssgManifest.js`,
`404.html`,
`500.html`,
`foo.html`,
`index.html`,
];

const EXPECTED_PATTERNS = [
`_next\/static\/chunks\/[0-9]+-[^\.]+\.js`,
`_next\/static\/chunks\/app-internals-[^\.]+\.js`,
`_next\/static\/chunks\/app\/bar\/page-[^\.]+\.js`,
`_next\/static\/chunks\/app\/foo\/page-[^\.]+\.js`,
`_next\/static\/chunks\/app\/layout-[^\.]+\.js`,
`_next\/static\/chunks\/main-[^\.]+\.js`,
`_next\/static\/chunks\/main-app-[^\.]+\.js`,
`_next\/static\/chunks\/pages\/_app-[^\.]+\.js`,
`_next\/static\/chunks\/pages\/_error-[^\.]+\.js`,
`_next\/static\/chunks\/pages\/about\/me-[^\.]+\.js`,
`_next\/static\/chunks\/pages\/index-[^\.]+\.js`,
`_next\/static\/chunks\/polyfills-[^\.]+\.js`,
`_next\/static\/chunks\/webpack-[^\.]+\.js`,
`_next\/static\/css\/[^\.]+\.css`,
].map((it) => new RegExp(it));

const files = await getFilesListFromDir(`${DOT_FIREBASE_FOLDER_PATH}/hosting`);
const unmatchedFiles = files.filter(
(it) =>
!(EXPECTED_FILES.includes(it) || EXPECTED_PATTERNS.some((pattern) => !!it.match(pattern)))
);
const unmatchedExpectations = [
...EXPECTED_FILES.filter((it) => !files.includes(it)),
...EXPECTED_PATTERNS.filter((it) => !files.some((file) => !!file.match(it))),
];

expect(result.stdout, "deploy result").to.match(/file upload complete/);
expect(result.stdout, "deploy result").to.match(/found 20 files/);
expect(result.stdout, "deploy result").to.match(/Deploy complete!/);
expect(unmatchedFiles).to.be.empty;
expect(unmatchedExpectations).to.be.empty;
});
});
17 changes: 8 additions & 9 deletions src/frameworks/angular/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,20 @@ import { spawn } from "cross-spawn";
import { copy, pathExists } from "fs-extra";
import { mkdir } from "fs/promises";

import { BuildResult, Discovery, FrameworkType, SupportLevel } from "../interfaces";
import { promptOnce } from "../../prompt";
import {
BuildResult,
Discovery,
findDependency,
FrameworkType,
simpleProxy,
warnIfCustomBuildScript,
getNodeModuleBin,
relativeRequire,
SupportLevel,
} from "..";
import { promptOnce } from "../../prompt";
import { simpleProxy, warnIfCustomBuildScript } from "../utils";
findDependency,
} from "../utils";

export const name = "Angular";
export const support = SupportLevel.Experimental;
export const support = SupportLevel.Preview;
export const type = FrameworkType.Framework;
export const docsUrl = "https://firebase.google.com/docs/hosting/frameworks/angular";

const DEFAULT_BUILD_SCRIPT = ["ng build"];

Expand Down
13 changes: 6 additions & 7 deletions src/frameworks/astro/index.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import { sync as spawnSync, spawn } from "cross-spawn";
import { copy, existsSync } from "fs-extra";
import { join } from "path";
import { BuildResult, Discovery, FrameworkType, SupportLevel } from "../interfaces";
import { FirebaseError } from "../../error";
import {
BuildResult,
Discovery,
FrameworkType,
SupportLevel,
readJSON,
simpleProxy,
warnIfCustomBuildScript,
findDependency,
getNodeModuleBin,
} from "..";
import { FirebaseError } from "../../error";
import { readJSON, simpleProxy, warnIfCustomBuildScript } from "../utils";
} from "../utils";
import { getBootstrapScript, getConfig } from "./utils";

export const name = "Astro";
Expand Down
65 changes: 65 additions & 0 deletions src/frameworks/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { readdirSync, statSync } from "fs";
import { join } from "path";
import { Framework, SupportLevel } from "./interfaces";
import * as clc from "colorette";

export const NPM_COMMAND_TIMEOUT_MILLIES = 10_000;

export const SupportLevelWarnings = {
[SupportLevel.Experimental]: (framework: string) => `Thank you for trying our ${clc.italic(
"experimental"
)} support for ${framework} on Firebase Hosting.
${clc.yellow(`While this integration is maintained by Googlers it is not a supported Firebase product.
Issues filed on GitHub will be addressed on a best-effort basis by maintainers and other community members.`)}`,
[SupportLevel.Preview]: (framework: string) => `Thank you for trying our ${clc.italic(
"early preview"
)} of ${framework} support on Firebase Hosting.
${clc.yellow(
"During the preview, support is best-effort and breaking changes can be expected. Proceed with caution."
)}`,
};

export const DEFAULT_DOCS_URL =
"https://firebase.google.com/docs/hosting/frameworks/frameworks-overview";
export const FILE_BUG_URL =
"https://github.com/firebase/firebase-tools/issues/new?template=bug_report.md";
export const FEATURE_REQUEST_URL =
"https://github.com/firebase/firebase-tools/issues/new?template=feature_request.md";
export const MAILING_LIST_URL = "https://goo.gle/41enW5X";

export const FIREBASE_FRAMEWORKS_VERSION = "^0.9.0";
export const FIREBASE_FUNCTIONS_VERSION = "^4.3.0";
export const FIREBASE_ADMIN_VERSION = "^11.0.1";
export const NODE_VERSION = parseInt(process.versions.node, 10);
export const VALID_ENGINES = { node: [16, 18] };

export const DEFAULT_REGION = "us-central1";
export const ALLOWED_SSR_REGIONS = [
{ name: "us-central1 (Iowa)", value: "us-central1" },
{ name: "us-west1 (Oregon)", value: "us-west1" },
{ name: "us-east1 (South Carolina)", value: "us-east1" },
{ name: "europe-west1 (Belgium)", value: "europe-west1" },
{ name: "asia-east1 (Taiwan)", value: "asia-east1" },
];

export const WebFrameworks: Record<string, Framework> = Object.fromEntries(
readdirSync(__dirname)
.filter((path) => statSync(join(__dirname, path)).isDirectory())
.map((path) => {
// If not called by the CLI, (e.g., by the VS Code Extension)
// __dirname won't refer to this folder and these files won't be available.
// Instead it may find sibling folders that aren't modules, and this
// require will throw.
// Long term fix may be to bundle this instead of reading files at runtime
// but for now, this prevents crashing.
try {
return [path, require(join(__dirname, path))];
} catch (e) {
return [];
}
})
.filter(
([, obj]) =>
obj && obj.name && obj.discover && obj.build && obj.type !== undefined && obj.support
)
);
5 changes: 3 additions & 2 deletions src/frameworks/express/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@ import { execSync } from "child_process";
import { copy, pathExists } from "fs-extra";
import { mkdir, readFile } from "fs/promises";
import { join } from "path";
import { BuildResult, FrameworkType, SupportLevel } from "..";
import { BuildResult, FrameworkType, SupportLevel } from "../interfaces";

// Use "true &&"" to keep typescript from compiling this file and rewriting
// the import statement into a require
const { dynamicImport } = require(true && "../../dynamicImport");

export const name = "Express.js";
export const support = SupportLevel.Experimental;
export const support = SupportLevel.Preview;
export const type = FrameworkType.Custom;
export const docsUrl = "https://firebase.google.com/docs/hosting/frameworks/express";

async function getConfig(root: string) {
const packageJsonBuffer = await readFile(join(root, "package.json"));
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 @@ -4,7 +4,7 @@ import { join } from "path";
import { load as loadYaml } from "js-yaml";
import { readFile } from "fs/promises";

import { BuildResult, Discovery, FrameworkType, SupportLevel } from "..";
import { BuildResult, Discovery, FrameworkType, SupportLevel } from "../interfaces";
import { FirebaseError } from "../../error";
import { assertFlutterCliExists } from "./utils";

Expand Down
Loading