Skip to content

Commit bbab473

Browse files
committed
Merge branch 'main' into chalosalvador/upgrade-nextjs-15
2 parents f330d80 + f938deb commit bbab473

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+4604
-5026
lines changed

.github/workflows/test.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ jobs:
5656
needs: build
5757
strategy:
5858
matrix:
59-
node: [18, 20]
59+
node: [18, 20, 22]
6060
fail-fast: false
6161
name: Test Node ${{ matrix.node }}
6262
steps:
@@ -94,6 +94,7 @@ jobs:
9494
run: rsync -a artifact/ packages
9595
- name: Test
9696
run: npm run test
97+
continue-on-error: ${{ matrix.node == 22 }}
9798

9899
lint:
99100
runs-on: ubuntu-latest

CODEOWNERS

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44

55
# App Hosting Platform Adapters code
66
# TODO add group for App Hosting
7-
/packages/@apphosting/adapter-*/ @Yuangwang @sjjj986 @jamesdaniels @tonyjhuang @blidd-google
7+
/packages/@apphosting/adapter-*/ @Yuangwang @sjjj986 @jamesdaniels @tonyjhuang @blidd-google @taeold
8+
/packages/@apphosting/common/ @Yuangwang @sjjj986 @jamesdaniels @tonyjhuang @blidd-google @taeold
89

910
# Web Frameworks code
1011
/packages/@apphosting/build/ @FirebaseExtended/firebase-full-time-employees @FirebaseExtended/monogram

package-lock.json

Lines changed: 59 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
},
1313
"workspaces": [
1414
"packages/@apphosting/*",
15-
"packages/create-next-on-firebase/*",
15+
"packages/create-next-on-firebase",
1616
"packages/firebase-frameworks"
1717
],
1818
"devDependencies": {
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +0,0 @@
1-
/e2e
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
runs
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import assert from "assert";
2+
import { posix } from "path";
3+
4+
export const host = process.env.HOST;
5+
6+
if (!host) {
7+
throw new Error("HOST environment variable expected");
8+
}
9+
10+
const isLocalhost = new URL(host).hostname === "localhost";
11+
12+
describe("common", () => {
13+
it("/", async () => {
14+
const response = await fetch(host);
15+
assert.ok(response.ok);
16+
assert.equal(response.headers.get("content-type")?.toLowerCase(), "text/html; charset=utf-8");
17+
assert.equal(response.headers.get("cache-control"), null);
18+
});
19+
20+
it("/favicon.ico", async () => {
21+
const response = await fetch(posix.join(host, "favicon.ico"));
22+
assert.ok(response.ok);
23+
assert.equal(response.headers.get("content-type"), "image/x-icon");
24+
assert.equal(
25+
response.headers.get("cache-control"),
26+
isLocalhost ? "public, max-age=31536000" : "public,max-age=60",
27+
);
28+
});
29+
30+
it(`404`, async () => {
31+
const response = await fetch(posix.join(host, Math.random().toString()));
32+
assert.ok(response.ok);
33+
assert.equal(response.headers.get("content-type")?.toLowerCase(), "text/html; charset=utf-8");
34+
assert.equal(response.headers.get("cache-control"), null);
35+
});
36+
});
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
import { cp, readFile, writeFile } from "fs/promises";
2+
import promiseSpawn from "@npmcli/promise-spawn";
3+
import { dirname, join, relative } from "path";
4+
import { fileURLToPath } from "url";
5+
import { parse as parseYaml } from "yaml";
6+
import { spawn } from "child_process";
7+
import fsExtra from "fs-extra";
8+
9+
const { readFileSync, mkdirp, readJSON, writeJSON, rmdir } = fsExtra;
10+
const __dirname = dirname(fileURLToPath(import.meta.url));
11+
12+
const starterTemplateDir = "../../../starters/angular/basic";
13+
14+
const errors: any[] = [];
15+
16+
await rmdir(join(__dirname, "runs"), { recursive: true }).catch(() => undefined);
17+
18+
console.log("\nBuilding and starting test projects in parallel...");
19+
20+
const tests = await Promise.all(
21+
[
22+
[false, false],
23+
[false, true],
24+
[true, false],
25+
[true, true],
26+
].map(async ([enableSSR, enableSSG]) => {
27+
const runId = Math.random().toString().split(".")[1];
28+
const cwd = join(__dirname, "runs", runId);
29+
await mkdirp(cwd);
30+
31+
console.log(`[${runId}] Copying ${starterTemplateDir} to working directory`);
32+
await cp(starterTemplateDir, cwd, { recursive: true });
33+
34+
const packageJSON = await readJSON(join(cwd, "package.json"));
35+
packageJSON.name = `firebase-app-hosting-angular-${runId}`;
36+
await writeJSON(join(cwd, "package.json"), packageJSON);
37+
38+
console.log(`[${runId}] > npm ci --silent --no-progress`);
39+
await promiseSpawn("npm", ["ci", "--silent", "--no-progress"], {
40+
cwd,
41+
stdio: "inherit",
42+
shell: true,
43+
});
44+
45+
const angularJSON = JSON.parse((await readFile(join(cwd, "angular.json"))).toString());
46+
47+
if (!enableSSR) {
48+
console.log(`[${runId}] Disabling SSR option in angular.json`);
49+
angularJSON.projects["firebase-app-hosting-angular"].architect.build.options.ssr = false;
50+
}
51+
if (!enableSSG) {
52+
console.log(`[${runId}] Disabling prerender option in angular.json`);
53+
angularJSON.projects["firebase-app-hosting-angular"].architect.build.options.prerender =
54+
false;
55+
}
56+
await writeFile(join(cwd, "angular.json"), JSON.stringify(angularJSON, null, 2));
57+
58+
const buildScript = relative(cwd, join(__dirname, "../dist/bin/build.js"));
59+
console.log(`[${runId}] > node ${buildScript}`);
60+
61+
const frameworkVersion = JSON.parse(
62+
readFileSync(join(cwd, "node_modules", "@angular", "core", "package.json"), "utf-8"),
63+
).version;
64+
await promiseSpawn("node", [buildScript], {
65+
cwd,
66+
stdio: "inherit",
67+
shell: true,
68+
env: {
69+
...process.env,
70+
FRAMEWORK_VERSION: frameworkVersion,
71+
},
72+
});
73+
74+
const bundleYaml = parseYaml(readFileSync(join(cwd, ".apphosting/bundle.yaml")).toString());
75+
76+
const runCommand = bundleYaml.runConfig.runCommand;
77+
78+
if (typeof runCommand !== "string") {
79+
throw new Error("runCommand must be a string");
80+
}
81+
82+
const [runScript, ...runArgs] = runCommand.split(" ");
83+
let resolveHostname: (it: string) => void;
84+
let rejectHostname: () => void;
85+
const hostnamePromise = new Promise<string>((resolve, reject) => {
86+
resolveHostname = resolve;
87+
rejectHostname = reject;
88+
});
89+
const port = 8080 + Math.floor(Math.random() * 1000);
90+
console.log(`[${runId}] > PORT=${port} ${runCommand}`);
91+
const run = spawn(runScript, runArgs, {
92+
cwd,
93+
shell: true,
94+
env: {
95+
...process.env,
96+
NODE_ENV: "production",
97+
PORT: port.toString(),
98+
},
99+
});
100+
run.stderr.on("data", (data) => console.error(data.toString()));
101+
run.stdout.on("data", (data) => {
102+
console.log(data.toString());
103+
if (data.toString() === `Node Express server listening on http://localhost:${port}\n`) {
104+
resolveHostname(`http://localhost:${port}`);
105+
} else {
106+
run.stdin.end();
107+
run.kill("SIGKILL");
108+
}
109+
});
110+
run.on("close", (code) => {
111+
if (code) {
112+
rejectHostname();
113+
}
114+
});
115+
const host = await hostnamePromise;
116+
117+
return [host, run, enableSSR, enableSSG] as const;
118+
}),
119+
);
120+
121+
console.log("\n\n");
122+
123+
for (const [host, run, enableSSR, enableSSG] of tests) {
124+
try {
125+
console.log(
126+
`> HOST=${host}${enableSSR ? " SSR=1" : ""}${
127+
enableSSG ? " SSG=1" : ""
128+
} ts-mocha -p tsconfig.json e2e/*.spec.ts`,
129+
);
130+
await promiseSpawn("ts-mocha", ["-p", "tsconfig.json", "e2e/*.spec.ts"], {
131+
shell: true,
132+
stdio: "inherit",
133+
env: {
134+
...process.env,
135+
SSR: enableSSR ? "1" : undefined,
136+
SSG: enableSSG ? "1" : undefined,
137+
HOST: host,
138+
},
139+
}).finally(() => {
140+
run.stdin.end();
141+
run.kill("SIGKILL");
142+
});
143+
} catch (e) {
144+
errors.push(e);
145+
}
146+
}
147+
148+
if (errors.length) {
149+
console.error(errors);
150+
process.exit(1);
151+
}
152+
153+
process.exit(0);
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import assert from "assert";
2+
import { posix } from "path";
3+
4+
const host = process.env.HOST;
5+
6+
if (!host) {
7+
throw new Error("HOST environment variable expected");
8+
}
9+
10+
describe("single-page app", () => {
11+
before(function () {
12+
if (process.env.SSR || process.env.SSG) {
13+
// eslint-disable-next-line @typescript-eslint/no-invalid-this
14+
this.skip();
15+
}
16+
});
17+
18+
// eslint-disable-next-line prefer-arrow-callback
19+
it("/", async () => {
20+
const response = await fetch(host);
21+
const body = await response.text();
22+
assert(body.includes("<app-root></app-root>"));
23+
});
24+
25+
it(`404`, async () => {
26+
const response = await fetch(posix.join(host, Math.random().toString()));
27+
const body = await response.text();
28+
assert(body.includes("<app-root></app-root>"));
29+
});
30+
});

0 commit comments

Comments
 (0)