diff --git a/.changeset/lemon-donkeys-tell.md b/.changeset/lemon-donkeys-tell.md new file mode 100644 index 00000000..ac3d6d11 --- /dev/null +++ b/.changeset/lemon-donkeys-tell.md @@ -0,0 +1,5 @@ +--- +"@opennextjs/cloudflare": patch +--- + +`patchNextMinimal` is no more required with the latest `@opennextjs/aws` bump diff --git a/packages/cloudflare/src/cli/build/bundle-server.ts b/packages/cloudflare/src/cli/build/bundle-server.ts index f04512f2..791eeb33 100644 --- a/packages/cloudflare/src/cli/build/bundle-server.ts +++ b/packages/cloudflare/src/cli/build/bundle-server.ts @@ -16,7 +16,6 @@ import { inlineEvalManifest } from "./patches/plugins/eval-manifest.js"; import { inlineFindDir } from "./patches/plugins/find-dir.js"; import { patchInstrumentation } from "./patches/plugins/instrumentation.js"; import { inlineLoadManifest } from "./patches/plugins/load-manifest.js"; -import { patchNextMinimal } from "./patches/plugins/next-minimal.js"; import { handleOptionalDependencies } from "./patches/plugins/optional-deps.js"; import { patchDepdDeprecations } from "./patches/plugins/patch-depd-deprecations.js"; import { fixRequire } from "./patches/plugins/require.js"; @@ -99,7 +98,6 @@ export async function bundleServer(buildOpts: BuildOptions): Promise { inlineLoadManifest(updater, buildOpts), inlineBuildId(updater), patchDepdDeprecations(updater), - patchNextMinimal(updater), // Apply updater updates, must be the last plugin updater.plugin, ] as Plugin[], diff --git a/packages/cloudflare/src/cli/build/patches/plugins/next-minimal.spec.ts b/packages/cloudflare/src/cli/build/patches/plugins/next-minimal.spec.ts deleted file mode 100644 index 90cf924d..00000000 --- a/packages/cloudflare/src/cli/build/patches/plugins/next-minimal.spec.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { patchCode } from "@opennextjs/aws/build/patch/astCodePatcher.js"; -import { describe, expect, test } from "vitest"; - -import { abortControllerRule } from "./next-minimal"; - -const appPageRuntimeProdJs = `let p = new AbortController; -async function h(e3, t3) { - let { flightRouterState: r3, nextUrl: a2, prefetchKind: i2 } = t3, u2 = { [n2.hY]: "1", [n2.B]: encodeURIComponent(JSON.stringify(r3)) }; - i2 === o.ob.AUTO && (u2[n2._V] = "1"), a2 && (u2[n2.kO] = a2); - try { - var c2; - let t4 = i2 ? i2 === o.ob.TEMPORARY ? "high" : "low" : "auto"; - "export" === process.env.__NEXT_CONFIG_OUTPUT && ((e3 = new URL(e3)).pathname.endsWith("/") ? e3.pathname += "index.txt" : e3.pathname += ".txt"); - let r4 = await m(e3, u2, t4, p.signal), a3 = d(r4.url), h2 = r4.redirected ? a3 : void 0, g = r4.headers.get("content-type") || "", v = !!(null == (c2 = r4.headers.get("vary")) ? void 0 : c2.includes(n2.kO)), b = !!r4.headers.get(n2.jc), S = r4.headers.get(n2.UK), _ = null !== S ? parseInt(S, 10) : -1, w = g.startsWith(n2.al); - if ("export" !== process.env.__NEXT_CONFIG_OUTPUT || w || (w = g.startsWith("text/plain")), !w || !r4.ok || !r4.body) - return e3.hash && (a3.hash = e3.hash), f(a3.toString()); - let k = b ? function(e4) { - let t5 = e4.getReader(); - return new ReadableStream({ async pull(e5) { - for (; ; ) { - let { done: r5, value: n3 } = await t5.read(); - if (!r5) { - e5.enqueue(n3); - continue; - } - return; - } - } }); - }(r4.body) : r4.body, E = await y(k); - if ((0, l.X)() !== E.b) - return f(r4.url); - return { flightData: (0, s.aj)(E.f), canonicalUrl: h2, couldBeIntercepted: v, prerendered: E.S, postponed: b, staleTime: _ }; - } catch (t4) { - return p.signal.aborted || console.error("Failed to fetch RSC payload for " + e3 + ". Falling back to browser navigation.", t4), { flightData: e3.toString(), canonicalUrl: void 0, couldBeIntercepted: false, prerendered: false, postponed: false, staleTime: -1 }; - } -} -`; - -describe("Abort controller", () => { - test("minimal", () => { - expect(patchCode(appPageRuntimeProdJs, abortControllerRule)).toBe( - `let p = {signal:{aborted: false}}; -async function h(e3, t3) { - let { flightRouterState: r3, nextUrl: a2, prefetchKind: i2 } = t3, u2 = { [n2.hY]: "1", [n2.B]: encodeURIComponent(JSON.stringify(r3)) }; - i2 === o.ob.AUTO && (u2[n2._V] = "1"), a2 && (u2[n2.kO] = a2); - try { - var c2; - let t4 = i2 ? i2 === o.ob.TEMPORARY ? "high" : "low" : "auto"; - "export" === process.env.__NEXT_CONFIG_OUTPUT && ((e3 = new URL(e3)).pathname.endsWith("/") ? e3.pathname += "index.txt" : e3.pathname += ".txt"); - let r4 = await m(e3, u2, t4, p.signal), a3 = d(r4.url), h2 = r4.redirected ? a3 : void 0, g = r4.headers.get("content-type") || "", v = !!(null == (c2 = r4.headers.get("vary")) ? void 0 : c2.includes(n2.kO)), b = !!r4.headers.get(n2.jc), S = r4.headers.get(n2.UK), _ = null !== S ? parseInt(S, 10) : -1, w = g.startsWith(n2.al); - if ("export" !== process.env.__NEXT_CONFIG_OUTPUT || w || (w = g.startsWith("text/plain")), !w || !r4.ok || !r4.body) - return e3.hash && (a3.hash = e3.hash), f(a3.toString()); - let k = b ? function(e4) { - let t5 = e4.getReader(); - return new ReadableStream({ async pull(e5) { - for (; ; ) { - let { done: r5, value: n3 } = await t5.read(); - if (!r5) { - e5.enqueue(n3); - continue; - } - return; - } - } }); - }(r4.body) : r4.body, E = await y(k); - if ((0, l.X)() !== E.b) - return f(r4.url); - return { flightData: (0, s.aj)(E.f), canonicalUrl: h2, couldBeIntercepted: v, prerendered: E.S, postponed: b, staleTime: _ }; - } catch (t4) { - return p.signal.aborted || console.error("Failed to fetch RSC payload for " + e3 + ". Falling back to browser navigation.", t4), { flightData: e3.toString(), canonicalUrl: void 0, couldBeIntercepted: false, prerendered: false, postponed: false, staleTime: -1 }; - } -} -` - ); - }); -}); diff --git a/packages/cloudflare/src/cli/build/patches/plugins/next-minimal.ts b/packages/cloudflare/src/cli/build/patches/plugins/next-minimal.ts deleted file mode 100644 index f6a33d19..00000000 --- a/packages/cloudflare/src/cli/build/patches/plugins/next-minimal.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { patchCode } from "@opennextjs/aws/build/patch/astCodePatcher.js"; -import { ContentUpdater, type Plugin } from "@opennextjs/aws/plugins/content-updater.js"; - -// Remove an instantiation of `AbortController` from the runtime. -// -// Solves https://github.com/cloudflare/workerd/issues/3657: -// - The `AbortController` is meant for the client side, but ends in the server code somehow. -// That's why we can get ride of it. See https://github.com/vercel/next.js/pull/73975/files. -// - Top level instantiation of `AbortController` are not supported by workerd as of March, 2025. -// See https://github.com/cloudflare/workerd/issues/3657 -// - As Next code is not more executed at top level, we do not need to apply this patch -// See https://github.com/opennextjs/opennextjs-cloudflare/pull/497 -// -// We try to be as specific as possible to avoid patching the wrong thing here -export const abortControllerRule = ` -rule: - all: - - kind: lexical_declaration - pattern: let $VAR = new AbortController - - precedes: - kind: function_declaration - stopBy: end - has: - kind: statement_block - has: - kind: try_statement - has: - kind: catch_clause - has: - kind: statement_block - has: - kind: return_statement - all: - - has: - stopBy: end - kind: member_expression - pattern: $VAR.signal.aborted - - has: - stopBy: end - kind: call_expression - regex: console.error\\("Failed to fetch RSC payload for - -fix: - 'let $VAR = {signal:{aborted: false}};' -`; - -// This rule is used instead of defining `process.env.NEXT_MINIMAL` in the `esbuild config. -// Do we want to entirely replace these functions to reduce the bundle size? -// In next `renderHTML` is used as a fallback in case of errors, but in minimal mode it just throws the error and the responsibility of handling it is on the infra. -export const nextMinimalRule = ` -rule: - kind: member_expression - pattern: process.env.NEXT_MINIMAL - any: - - inside: - kind: parenthesized_expression - stopBy: end - inside: - kind: if_statement - any: - - inside: - kind: statement_block - inside: - kind: method_definition - any: - - has: {kind: property_identifier, field: name, regex: runEdgeFunction} - - has: {kind: property_identifier, field: name, regex: runMiddleware} - - has: {kind: property_identifier, field: name, regex: imageOptimizer} - - has: - kind: statement_block - has: - kind: expression_statement - pattern: res.statusCode = 400; -fix: - 'true' -`; - -export function patchNextMinimal(updater: ContentUpdater): Plugin { - return updater.updateContent("patch-next-minimal", [ - { - field: { - filter: /next-server\.(js)$/, - contentFilter: /.*/, - callback: ({ contents }) => { - return patchCode(contents, nextMinimalRule); - }, - }, - }, - ]); -}