From dc41d9c644be830161e2c6c04a19e40ba30eb0ed Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Thu, 29 Feb 2024 16:30:56 -0800 Subject: [PATCH] Add param to debug PPR skeleton in dev (#62703) This adds an experimental query `__nextppronly` to allow debugging PPR skeletons in development to avoid having to do numerous builds to be able to debug this experience. x-ref: [slack thread](https://vercel.slack.com/archives/C05KYT5S9FF/p1709151588583179?thread_ts=1708474869.960689&cid=C05KYT5S9FF) --- packages/next/src/server/app-render/types.ts | 1 + packages/next/src/server/base-server.ts | 26 +++++++++++++++++++- test/e2e/app-dir/app/app/skeleton/page.js | 20 +++++++++++++++ test/e2e/app-dir/app/index.test.ts | 11 +++++++++ 4 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 test/e2e/app-dir/app/app/skeleton/page.js diff --git a/packages/next/src/server/app-render/types.ts b/packages/next/src/server/app-render/types.ts index 59b7c9b7ed6cb..2f8bf9b28e692 100644 --- a/packages/next/src/server/app-render/types.ts +++ b/packages/next/src/server/app-render/types.ts @@ -150,6 +150,7 @@ export interface RenderOptsPartial { swrDelta: SwrDelta | undefined } postponed?: string + isStaticGeneration?: boolean } export type RenderOpts = LoadComponentsReturnType & diff --git a/packages/next/src/server/base-server.ts b/packages/next/src/server/base-server.ts index d201ebe49811c..b03650aedf35a 100644 --- a/packages/next/src/server/base-server.ts +++ b/packages/next/src/server/base-server.ts @@ -2123,9 +2123,17 @@ export default abstract class Server { postponed: string | undefined }) => Promise + // allow debugging the skeleton in dev with PPR + // instead of continuing to resume stream right away + const debugPPRSkeleton = Boolean( + this.nextConfig.experimental.ppr && + this.renderOpts.dev && + query.__nextppronly + ) + const doRender: Renderer = async ({ postponed }) => { // In development, we always want to generate dynamic HTML. - const supportsDynamicHTML: boolean = + let supportsDynamicHTML: boolean = // If this isn't a data request and we're not in development, then we // support dynamic HTML. (!isDataReq && opts.dev === true) || @@ -2197,6 +2205,14 @@ export default abstract class Server { postponed, } + if (debugPPRSkeleton) { + supportsDynamicHTML = false + renderOpts.nextExport = true + renderOpts.supportsDynamicHTML = false + renderOpts.isStaticGeneration = true + renderOpts.isRevalidate = true + } + // Legacy render methods will return a render result that needs to be // served by the server. let result: RenderResult @@ -2855,6 +2871,14 @@ export default abstract class Server { } } + if (debugPPRSkeleton) { + return { + type: 'html', + body, + revalidate: 0, + } + } + // This request has postponed, so let's create a new transformer that the // dynamic data can pipe to that will attach the dynamic data to the end // of the response. diff --git a/test/e2e/app-dir/app/app/skeleton/page.js b/test/e2e/app-dir/app/app/skeleton/page.js new file mode 100644 index 0000000000000..01dc67975f22e --- /dev/null +++ b/test/e2e/app-dir/app/app/skeleton/page.js @@ -0,0 +1,20 @@ +import { cookies } from 'next/headers' +import { Suspense } from 'react' + +async function Suspend() { + await new Promise((resolve) => { + setTimeout(resolve, 3000) + }) + cookies() + return

suspended content

+} + +export default function Page() { + return ( +
+ + + +
+ ) +} diff --git a/test/e2e/app-dir/app/index.test.ts b/test/e2e/app-dir/app/index.test.ts index a02815e5bdcfc..3de55b71a5400 100644 --- a/test/e2e/app-dir/app/index.test.ts +++ b/test/e2e/app-dir/app/index.test.ts @@ -20,6 +20,17 @@ createNextDescribe( }, }, ({ next, isNextDev: isDev, isNextStart, isNextDeploy, isTurbopack }) => { + if (isDev && isPPREnabledByDefault) { + it('should allow returning just skeleton in dev with query', async () => { + const res = await next.fetch('/skeleton?__nextppronly=1') + expect(res.status).toBe(200) + + const html = await res.text() + expect(html).toContain('Skeleton') + expect(html).not.toContain('suspended content') + }) + } + if (process.env.NEXT_EXPERIMENTAL_COMPILE) { it('should provide query for getStaticProps page correctly', async () => { const res = await next.fetch('/ssg?hello=world')