···22import { globSync } from "glob";
33import { NextjsAppPaths } from "../../../../nextjs-paths";
4455-export function patchReadFile(
66- code: string,
77- nextjsAppPaths: NextjsAppPaths
88-): string {
99- console.log("# patchReadFile");
1010- // The next-server code gets the buildId from the filesystem, resulting in a `[unenv] fs.readFileSync is not implemented yet!` error
1111- // so we add an early return to the `getBuildId` function so that the `readyFileSync` is never encountered
1212- // (source: https://github.com/vercel/next.js/blob/15aeb92efb34c09a36/packages/next/src/server/next-server.ts#L438-L451)
1313- // Note: we could/should probably just patch readFileSync here or something!
1414- code = code.replace(
1515- "getBuildId() {",
1616- `getBuildId() {
1717- return ${JSON.stringify(
1818- readFileSync(
1919- `${nextjsAppPaths.standaloneAppDotNextDir}/BUILD_ID`,
2020- "utf-8"
2121- )
2222- )};
55+export function patchReadFile(code: string, nextjsAppPaths: NextjsAppPaths): string {
66+ console.log("# patchReadFile");
77+ // The next-server code gets the buildId from the filesystem, resulting in a `[unenv] fs.readFileSync is not implemented yet!` error
88+ // so we add an early return to the `getBuildId` function so that the `readyFileSync` is never encountered
99+ // (source: https://github.com/vercel/next.js/blob/15aeb92efb34c09a36/packages/next/src/server/next-server.ts#L438-L451)
1010+ // Note: we could/should probably just patch readFileSync here or something!
1111+ code = code.replace(
1212+ "getBuildId() {",
1313+ `getBuildId() {
1414+ return ${JSON.stringify(readFileSync(`${nextjsAppPaths.standaloneAppDotNextDir}/BUILD_ID`, "utf-8"))};
2315 `
2424- );
1616+ );
25172626- // Same as above, the next-server code loads the manifests with `readyFileSync` and we want to avoid that
2727- // (source: https://github.com/vercel/next.js/blob/15aeb92e/packages/next/src/server/load-manifest.ts#L34-L56)
2828- // Note: we could/should probably just patch readFileSync here or something!
2929- const manifestJsons = globSync(
3030- `${nextjsAppPaths.standaloneAppDotNextDir}/**/*-manifest.json`
3131- ).map((file) => file.replace(nextjsAppPaths.standaloneAppDir + "/", ""));
3232- code = code.replace(
3333- /function loadManifest\((.+?), .+?\) {/,
3434- `$&
1818+ // Same as above, the next-server code loads the manifests with `readyFileSync` and we want to avoid that
1919+ // (source: https://github.com/vercel/next.js/blob/15aeb92e/packages/next/src/server/load-manifest.ts#L34-L56)
2020+ // Note: we could/should probably just patch readFileSync here or something!
2121+ const manifestJsons = globSync(`${nextjsAppPaths.standaloneAppDotNextDir}/**/*-manifest.json`).map((file) =>
2222+ file.replace(nextjsAppPaths.standaloneAppDir + "/", "")
2323+ );
2424+ code = code.replace(
2525+ /function loadManifest\((.+?), .+?\) {/,
2626+ `$&
3527 ${manifestJsons
3636- .map(
3737- (manifestJson) => `
2828+ .map(
2929+ (manifestJson) => `
3830 if ($1.endsWith("${manifestJson}")) {
3939- return ${readFileSync(
4040- `${nextjsAppPaths.standaloneAppDir}/${manifestJson}`,
4141- "utf-8"
4242- )};
3131+ return ${readFileSync(`${nextjsAppPaths.standaloneAppDir}/${manifestJson}`, "utf-8")};
4332 }
4433 `
4545- )
4646- .join("\n")}
3434+ )
3535+ .join("\n")}
4736 throw new Error("Unknown loadManifest: " + $1);
4837 `
4949- );
3838+ );
50395151- return code;
4040+ return code;
5241}
···33import { NextjsAppPaths } from "../../../../nextjs-paths";
4455export function patchWranglerDeps(paths: NextjsAppPaths) {
66- console.log("# patchWranglerDeps");
66+ console.log("# patchWranglerDeps");
7788- console.log({ base: paths.standaloneAppDotNextDir });
88+ console.log({ base: paths.standaloneAppDotNextDir });
991010- // Patch .next/standalone/node_modules/next/dist/compiled/next-server/pages.runtime.prod.js
1111- //
1212- // Remove the need for an alias in wrangler.toml:
1313- //
1414- // [alias]
1515- // # critters is `require`d from `pages.runtime.prod.js` when running wrangler dev, so we need to stub it out
1616- // "critters" = "./.next/standalone/node_modules/cf/templates/shims/empty.ts"
1717- const pagesRuntimeFile = path.join(
1818- paths.standaloneAppDir,
1919- "node_modules",
2020- "next",
2121- "dist",
2222- "compiled",
2323- "next-server",
2424- "pages.runtime.prod.js"
2525- );
1010+ // Patch .next/standalone/node_modules/next/dist/compiled/next-server/pages.runtime.prod.js
1111+ //
1212+ // Remove the need for an alias in wrangler.toml:
1313+ //
1414+ // [alias]
1515+ // # critters is `require`d from `pages.runtime.prod.js` when running wrangler dev, so we need to stub it out
1616+ // "critters" = "./.next/standalone/node_modules/cf/templates/shims/empty.ts"
1717+ const pagesRuntimeFile = path.join(
1818+ paths.standaloneAppDir,
1919+ "node_modules",
2020+ "next",
2121+ "dist",
2222+ "compiled",
2323+ "next-server",
2424+ "pages.runtime.prod.js"
2525+ );
26262727- const patchedPagesRuntime = fs
2828- .readFileSync(pagesRuntimeFile, "utf-8")
2929- .replace(`e.exports=require("critters")`, `e.exports={}`);
2727+ const patchedPagesRuntime = fs
2828+ .readFileSync(pagesRuntimeFile, "utf-8")
2929+ .replace(`e.exports=require("critters")`, `e.exports={}`);
30303131- fs.writeFileSync(pagesRuntimeFile, patchedPagesRuntime);
3131+ fs.writeFileSync(pagesRuntimeFile, patchedPagesRuntime);
32323333- // Patch .next/standalone/node_modules/next/dist/server/lib/trace/tracer.js
3434- //
3535- // Remove the need for an alias in wrangler.toml:
3636- //
3737- // [alias]
3838- // # @opentelemetry/api is `require`d when running wrangler dev, so we need to stub it out
3939- // # IMPORTANT: we shim @opentelemetry/api to the throwing shim so that it will throw right away, this is so that we throw inside the
4040- // # try block here: https://github.com/vercel/next.js/blob/9e8266a7/packages/next/src/server/lib/trace/tracer.ts#L27-L31
4141- // # causing the code to require the 'next/dist/compiled/@opentelemetry/api' module instead (which properly works)
4242- // #"@opentelemetry/api" = "./.next/standalone/node_modules/cf/templates/shims/throw.ts"
4343- const tracerFile = path.join(
4444- paths.standaloneAppDir,
4545- "node_modules",
4646- "next",
4747- "dist",
4848- "server",
4949- "lib",
5050- "trace",
5151- "tracer.js"
5252- );
3333+ // Patch .next/standalone/node_modules/next/dist/server/lib/trace/tracer.js
3434+ //
3535+ // Remove the need for an alias in wrangler.toml:
3636+ //
3737+ // [alias]
3838+ // # @opentelemetry/api is `require`d when running wrangler dev, so we need to stub it out
3939+ // # IMPORTANT: we shim @opentelemetry/api to the throwing shim so that it will throw right away, this is so that we throw inside the
4040+ // # try block here: https://github.com/vercel/next.js/blob/9e8266a7/packages/next/src/server/lib/trace/tracer.ts#L27-L31
4141+ // # causing the code to require the 'next/dist/compiled/@opentelemetry/api' module instead (which properly works)
4242+ // #"@opentelemetry/api" = "./.next/standalone/node_modules/cf/templates/shims/throw.ts"
4343+ const tracerFile = path.join(
4444+ paths.standaloneAppDir,
4545+ "node_modules",
4646+ "next",
4747+ "dist",
4848+ "server",
4949+ "lib",
5050+ "trace",
5151+ "tracer.js"
5252+ );
53535454- const pacthedTracer = fs
5555- .readFileSync(tracerFile, "utf-8")
5656- .replaceAll(
5757- /\w+\s*=\s*require\([^/]*opentelemetry.*\)/g,
5858- `throw new Error("@opentelemetry/api")`
5959- );
5454+ const pacthedTracer = fs
5555+ .readFileSync(tracerFile, "utf-8")
5656+ .replaceAll(/\w+\s*=\s*require\([^/]*opentelemetry.*\)/g, `throw new Error("@opentelemetry/api")`);
60576161- writeFileSync(tracerFile, pacthedTracer);
5858+ writeFileSync(tracerFile, pacthedTracer);
6259}
···11import { headers } from "next/headers";
2233export async function GET() {
44- // Note: we use headers just so that the route is not built as a static one
55- const headersList = headers();
66- const sayHi = !!headersList.get("should-say-hi");
77- return new Response(sayHi ? "Hi World!" : "Hello World!");
44+ // Note: we use headers just so that the route is not built as a static one
55+ const headersList = headers();
66+ const sayHi = !!headersList.get("should-say-hi");
77+ return new Response(sayHi ? "Hi World!" : "Hello World!");
88}
991010export async function POST(request: Request) {
1111- return new Response(`Hello post-World! body=${await request.text()}`);
1111+ return new Response(`Hello post-World! body=${await request.text()}`);
1212}
+7-7
examples/api/app/layout.js
···11export const metadata = {
22- title: "API hello-world",
33- description: "a simple api hello-world app",
22+ title: "API hello-world",
33+ description: "a simple api hello-world app",
44};
5566export default function RootLayout({ children }) {
77- return (
88- <html lang="en">
99- <body>{children}</body>
1010- </html>
1111- );
77+ return (
88+ <html lang="en">
99+ <body>{children}</body>
1010+ </html>
1111+ );
1212}
+11-11
examples/api/app/page.js
···11export default function Home() {
22- return (
33- <main>
44- <p>
55- This application doesn't have a UI, just a single api route (running in
66- the <code>node.js</code> runtime):
77- <a href="/api/hello">
88- <code>/api/hello</code>
99- </a>
1010- </p>
1111- </main>
1212- );
22+ return (
33+ <main>
44+ <p>
55+ This application doesn't have a UI, just a single api route (running in the <code>node.js</code>{" "}
66+ runtime):
77+ <a href="/api/hello">
88+ <code>/api/hello</code>
99+ </a>
1010+ </p>
1111+ </main>
1212+ );
1313}
+8-8
examples/api/e2e-tests/base.spec.ts
···11import { test, expect } from "@playwright/test";
2233test("the application's noop index page is visible and it allows navigating to the hello-world api route", async ({
44- page,
44+ page,
55}) => {
66- await page.goto("/");
77- await expect(page.getByText("This application doesn't have")).toBeVisible();
88- await page.getByRole("link", { name: "/api/hello" }).click();
99- await expect(page.getByText("Hello World!")).toBeVisible();
66+ await page.goto("/");
77+ await expect(page.getByText("This application doesn't have")).toBeVisible();
88+ await page.getByRole("link", { name: "/api/hello" }).click();
99+ await expect(page.getByText("Hello World!")).toBeVisible();
1010});
11111212test("the hello-world api route works as intended", async ({ page }) => {
1313- const res = await page.request.get("/api/hello");
1414- expect(res.headers()["content-type"]).toContain("text/plain");
1515- expect(await res.text()).toEqual("Hello World!");
1313+ const res = await page.request.get("/api/hello");
1414+ expect(res.headers()["content-type"]).toContain("text/plain");
1515+ expect(await res.text()).toEqual("Hello World!");
1616});
+6-6
examples/api/next.config.mjs
···11/** @type {import('next').NextConfig} */
22const nextConfig = {
33- output: "standalone",
44- experimental: {
55- // IMPORTANT: this option is necessary for the chunks hack since that relies on the webpack-runtime.js file not being minified
66- // (since we regex-replace relying on specific variable names)
77- serverMinification: false,
88- },
33+ output: "standalone",
44+ experimental: {
55+ // IMPORTANT: this option is necessary for the chunks hack since that relies on the webpack-runtime.js file not being minified
66+ // (since we regex-replace relying on specific variable names)
77+ serverMinification: false,
88+ },
99};
10101111export default nextConfig;