diff --git a/.changeset/fix-preview-props-manifest.md b/.changeset/fix-preview-props-manifest.md new file mode 100644 index 000000000..e21db1012 --- /dev/null +++ b/.changeset/fix-preview-props-manifest.md @@ -0,0 +1,14 @@ +--- +"@opennextjs/cloudflare": patch +--- + +fix(patches): include `preview-props.json` in loadManifest build-time inlining + +Next.js 16.4 moved preview props out of `prerender-manifest.json` into a standalone +`.next/server/preview-props.json` (`PREVIEW_PROPS_MANIFEST`), loaded unconditionally by +`NextNodeServer.getPreviewProps()` from the `Server` constructor. The file exists in the build +output but wasn't matched by the glob pattern `*-manifest.json`, so the patched `loadManifest()` +threw at runtime and every dynamic route returned a 500. + +It is inlined rather than falling back to `{}` because it holds the actual draft-mode +id and signing/encryption keys. diff --git a/packages/cloudflare/src/cli/build/patches/plugins/load-manifest.ts b/packages/cloudflare/src/cli/build/patches/plugins/load-manifest.ts index 143dd544f..fa8907346 100644 --- a/packages/cloudflare/src/cli/build/patches/plugins/load-manifest.ts +++ b/packages/cloudflare/src/cli/build/patches/plugins/load-manifest.ts @@ -39,8 +39,13 @@ async function getLoadManifestRule(buildOpts: BuildOptions) { const baseDir = join(outputDir, "server-functions/default", getPackagePath(buildOpts)); const dotNextDir = join(baseDir, ".next"); + // Most server manifests are named `*-manifest.json`, but a few are not: + // `required-server-files.json`, `prefetch-hints.json` (Next 16.2+) and + // `server/preview-props.json` (Next 16.4+, previously inlined in the + // prerender manifest). They are loaded unconditionally by `NextNodeServer`, + // so they must be inlined rather than falling back to `{}`. const manifests = await glob( - join(dotNextDir, "**/{*-manifest,required-server-files,prefetch-hints}.json"), + join(dotNextDir, "**/{*-manifest,required-server-files,prefetch-hints,preview-props}.json"), { windowsPathsNoEscape: true, }