Skip to content

Commit 68115bc

Browse files
committed
Get dynamic content working
1 parent d1b4d0d commit 68115bc

File tree

4 files changed

+63
-62
lines changed

4 files changed

+63
-62
lines changed

packages/@apphosting/adapter-nextjs/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "wei-nextjs-adapter-test",
3-
"version": "15.0.10",
3+
"version": "15.0.13",
44
"main": "dist/index.js",
55
"description": "Experimental addon to the Firebase CLI to add web framework support",
66
"repository": {

packages/@apphosting/adapter-nextjs/src/bin/build.ts

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
#! /usr/bin/env node
22
import { build } from "esbuild";
3-
import { stringify } from "yaml"; // Add this import
3+
import { stringify } from "yaml";
44
import { spawn } from "child_process";
5-
import { join, dirname } from "path"; // Ensure dirname is imported
5+
import { join, dirname } from "path";
66
import fs from "fs-extra";
7-
import { fileURLToPath } from "url"; // Import this
7+
import { fileURLToPath } from "url";
88

9-
// 1. SHIM __dirname for ES Modules
109
const __filename = fileURLToPath(import.meta.url);
1110
const __dirname = dirname(__filename);
1211
export async function main() {
@@ -29,9 +28,6 @@ export async function main() {
2928
});
3029

3130
// 2. Move Standalone Output to .apphosting
32-
// The standalone build creates a folder structure that mirrors your hard drive
33-
// e.g., .next/standalone/Users/name/project/...
34-
// We need to find the actual project root inside standalone.
3531
const standaloneDir = join(root, ".next", "standalone");
3632
const outputDir = join(root, ".apphosting");
3733

@@ -77,7 +73,6 @@ export async function main() {
7773
// This runs the file we just bundled in step 3
7874
runCommand: "node .apphosting/adapter-server.js",
7975

80-
// Basic defaults - you can expose these as flags later if needed
8176
concurrency: 80,
8277
cpu: 1,
8378
memoryMiB: 512,
@@ -86,13 +81,12 @@ export async function main() {
8681
},
8782
metadata: {
8883
adapterPackageName: "wei-nextjs-adapter-test",
89-
adapterVersion: "15.0.3", // You might want to import this from package.json
84+
adapterVersion: "15.0.3",
85+
frameworkVersion: "16.0.1",
9086
framework: "nextjs",
9187
},
9288
outputFiles: {
9389
serverApp: {
94-
// We tell App Hosting to only upload/keep the .apphosting folder
95-
// since we moved everything important into it during Step 2.
9690
include: [".apphosting"]
9791
}
9892
}

packages/@apphosting/adapter-nextjs/src/bin/serve.ts

Lines changed: 57 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -2,72 +2,83 @@ import { createServer } from "http";
22
import { parse } from "url";
33
import path from "path";
44
import fs from "fs";
5-
// DELETE: import { fileURLToPath } from "url";
6-
7-
// DELETE these lines. They crash in CJS.
8-
// const __filename = fileURLToPath(import.meta.url);
9-
// const __dirname = path.dirname(__filename);
105

116
// 1. SET ENV VARS
127
// @ts-ignore
138
process.env['NODE_ENV'] = "production";
149
process.env['NEXT_PRIVATE_MINIMAL_MODE'] = "1";
1510

16-
console.log("🚀 Starting Native Adapter...");
17-
1811
async function start() {
19-
// CRITICAL FIX: Use the native __dirname directly.
20-
// We use @ts-ignore because TypeScript might complain if it thinks this is ESM.
2112
// @ts-ignore
2213
const serverDir = __dirname;
2314

24-
try {
25-
// 2. Load the Serialized Config relative to THIS script
26-
const configPath = path.join(serverDir, "firebase-next-config.json");
15+
// 2. IMPORT NEXT.JS INTERNALS (REQUIRED FOR PPR)
16+
// We need this symbol to attach the postponed state
17+
const nextMetaPath = require.resolve("next/dist/server/request-meta", { paths: [serverDir] });
18+
const { NEXT_REQUEST_META } = require(nextMetaPath);
19+
20+
// 3. LOAD CONFIG
21+
const configPath = path.join(serverDir, "firebase-next-config.json");
22+
const rawConfig = fs.readFileSync(configPath, 'utf-8');
23+
const buildContext = JSON.parse(rawConfig);
24+
25+
// Helper to find the postponed state for a path
26+
const getPostponedState = (path: string) => {
27+
// 1. Try exact match
28+
let prerender = buildContext.outputs.prerenders.find((it: any) => it.pathname === path);
2729

28-
console.log(`📥 Loading config from ${configPath}`);
29-
const rawConfig = fs.readFileSync(configPath, 'utf-8');
30-
const buildContext = JSON.parse(rawConfig);
30+
// 2. Try dynamic match
31+
if (!prerender) {
32+
const dynamicMatch = buildContext.routes.dynamicRoutes.find((it: any) =>
33+
path.match(new RegExp(it.sourceRegex))
34+
)?.source;
35+
prerender = buildContext.outputs.prerenders.find((it: any) => it.pathname === dynamicMatch);
36+
}
3137

32-
// 3. Resolve Next.js Server relative to THIS script
33-
const nextPath = require.resolve("next/dist/server/next-server", { paths: [serverDir] });
34-
const NextServer = require(nextPath).default;
38+
return prerender?.fallback?.postponedState;
39+
};
3540

36-
// 4. Initialize Server with the loaded config
37-
const server = new NextServer({
38-
dir: serverDir,
39-
hostname: '0.0.0.0',
40-
port: parseInt(process.env.PORT || "8080"),
41-
conf: buildContext.config
42-
});
41+
// 4. SETUP SERVER
42+
const nextPath = require.resolve("next/dist/server/next-server", { paths: [serverDir] });
43+
const NextServer = require(nextPath).default;
44+
45+
const server = new NextServer({
46+
dir: serverDir,
47+
hostname: '0.0.0.0',
48+
port: parseInt(process.env.PORT || "8080"),
49+
conf: buildContext.config,
50+
});
4351

44-
console.log("⏳ Preparing server...");
45-
const requestHandler = server.getRequestHandler();
46-
await server.prepare();
52+
await server.prepare();
53+
const requestHandler = server.getRequestHandler();
4754

48-
// 5. Start HTTP Listener
49-
createServer(async (req: any, res: any) => {
50-
try {
51-
const parsedUrl = parse(req.url, true);
55+
createServer(async (req: any, res: any) => {
56+
try {
57+
const parsedUrl = parse(req.url, true);
58+
const { pathname } = parsedUrl;
5259

53-
if (!req.headers['x-matched-path']) {
54-
req.headers['x-matched-path'] = parsedUrl.pathname;
60+
if (req.headers['next-resume'] === '1' && pathname) {
61+
const postponed = getPostponedState(pathname);
62+
if (postponed) {
63+
console.log(`⚡️ Injecting Postponed State for ${pathname}`);
64+
65+
req[NEXT_REQUEST_META] = {
66+
postponed: postponed
67+
};
5568
}
69+
}
5670

57-
await requestHandler(req, res, parsedUrl);
58-
} catch (err) {
59-
console.error("Request Error:", err);
60-
res.statusCode = 500;
61-
res.end("Internal Error");
71+
if (!req.headers['x-matched-path']) {
72+
req.headers['x-matched-path'] = pathname;
6273
}
63-
}).listen(parseInt(process.env.PORT || "8080"), () => {
64-
console.log(`> Ready on http://localhost:${process.env.PORT || 8080}`);
65-
});
6674

67-
} catch (e) {
68-
console.error("❌ Critical Error:", e);
69-
process.exit(1);
70-
}
75+
await requestHandler(req, res, parsedUrl);
76+
} catch (err) {
77+
console.error(err);
78+
res.statusCode = 500;
79+
res.end("Internal Error");
80+
}
81+
}).listen(parseInt(process.env.PORT || "8080"));
7182
}
7283

7384
start();

packages/@apphosting/adapter-nextjs/src/index.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import path from 'path';
55
const adapter: NextAdapter = {
66
name: 'firebase-apphosting-adapter',
77

8-
// Hook 1: Modify Config (Optional)
98
async modifyConfig(config, context) {
109
console.log(`🔌 [Adapter] Modifying config for phase: ${context.phase}`);
1110
// You can force settings here, e.g., enforcing standalone output
@@ -14,13 +13,11 @@ const adapter: NextAdapter = {
1413
output: 'standalone',
1514
experimental: {
1615
...config.experimental,
17-
// Helper to ensure ISR works correctly in some environments
1816
isrMemoryCacheSize: 0,
1917
}
2018
};
2119
},
2220

23-
// Hook 2: Build Complete (The specific feature you wanted)
2421
async onBuildComplete(data) {
2522
console.log('🔌 [Adapter] Build Complete. Serializing config...');
2623

@@ -33,5 +30,4 @@ async onBuildComplete(data) {
3330
}
3431
};
3532

36-
// Next.js expects a CommonJS export for the adapter
3733
export default adapter;

0 commit comments

Comments
 (0)