Skip to content

Commit

Permalink
[next] Ensure all prerenders have matching .action output (#11719)
Browse files Browse the repository at this point in the history
This ensures we create `.action` outputs for all prerendered paths that
point to a streaming serverless function when enabled so that our
routing rules have the expected destination available.

---------

Co-authored-by: Zack Tanner <1939140+ztanner@users.noreply.github.com>
  • Loading branch information
ijjk and ztanner committed Jun 11, 2024
1 parent 9d6088e commit 84b7445
Show file tree
Hide file tree
Showing 112 changed files with 312 additions and 249 deletions.
5 changes: 5 additions & 0 deletions .changeset/quiet-llamas-behave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@vercel/next': patch
---

Ensure all prerenders have matching .action output
1 change: 1 addition & 0 deletions packages/next/src/server-build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1364,6 +1364,7 @@ export async function serverBuild({
isCorrectNotFoundRoutes,
isEmptyAllowQueryForPrendered,
isAppPPREnabled,
hasActionOutputSupport,
});

await Promise.all(
Expand Down
9 changes: 9 additions & 0 deletions packages/next/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1953,6 +1953,7 @@ type OnPrerenderRouteArgs = {
isCorrectNotFoundRoutes?: boolean;
isEmptyAllowQueryForPrendered?: boolean;
isAppPPREnabled: boolean;
hasActionOutputSupport?: boolean;
};
let prerenderGroup = 1;

Expand Down Expand Up @@ -1990,6 +1991,7 @@ export const onPrerenderRoute =
isCorrectNotFoundRoutes,
isEmptyAllowQueryForPrendered,
isAppPPREnabled,
hasActionOutputSupport,
} = prerenderRouteArgs;

if (isBlocking && isFallback) {
Expand Down Expand Up @@ -2474,6 +2476,13 @@ export const onPrerenderRoute =
: {}),
});

if (hasActionOutputSupport) {
const actionOutputKey = `${path.join('./', srcRoute || '')}.action`;
if (srcRoute !== routeKey && lambdas[actionOutputKey]) {
lambdas[`${routeKey}.action`] = lambdas[actionOutputKey];
}
}

const normalizePathData = (pathData: string) => {
if (
(srcRoute === '/' || srcRoute == '/index') &&
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { revalidatePath } from 'next/cache';

export default async function Page() {
async function serverAction() {
'use server';
await new Promise(resolve => setTimeout(resolve, 1000));
revalidatePath('/dynamic');
}

return (
<form action={serverAction}>
<button>Submit</button>
</form>
);
}

export const dynamicParams = false;

export function generateStaticParams() {
return [{ slug: 'pre-generated' }];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { revalidatePath } from 'next/cache';

export default async function Page() {
async function serverAction() {
'use server';
await new Promise(resolve => setTimeout(resolve, 1000));
revalidatePath('/dynamic');
}

return (
<form action={serverAction}>
<button>Submit</button>
</form>
);
}

export const dynamicParams = false;

export function generateStaticParams() {
return [{ slug: 'pre-generated' }];
}
Original file line number Diff line number Diff line change
Expand Up @@ -208,44 +208,50 @@ describe(`${__dirname.split(path.sep).pop()}`, () => {
});

describe('generateStaticParams', () => {
it('should bypass the static cache for a server action when pre-generated', async () => {
const path = `${basePath}/rsc/static/generate-static-params/pre-generated`;
const dynamicPath = `${basePath}/rsc/static/generate-static-params/[slug]`;
const actionId = findActionId(dynamicPath, runtime);

const res = await fetch(
`${ctx.deploymentUrl}${path}`,
generateFormDataPayload(actionId)
);

expect(res.status).toEqual(200);
expect(res.headers.get('x-matched-path')).toBe(
dynamicPath + '.action'
);
expect(res.headers.get('content-type')).toBe('text/x-component');
if (runtime === 'node') {
expect(res.headers.get('x-vercel-cache')).toBe('MISS');
} else {
expect(res.headers.get('x-edge-runtime')).toBe('1');
}
});

it('should bypass the static cache for a server action when not pre-generated', async () => {
const page = `${basePath}/rsc/static/generate-static-params/[slug]`;
const actionId = findActionId(page, runtime);

const res = await fetch(
`${ctx.deploymentUrl}/${basePath}/rsc/static/generate-static-params/not-pre-generated`,
generateFormDataPayload(actionId)
);

expect(res.status).toEqual(200);
expect(res.headers.get('x-matched-path')).toBe(page + '.action');
expect(res.headers.get('content-type')).toBe('text/x-component');
if (runtime === 'node') {
expect(res.headers.get('x-vercel-cache')).toBe('MISS');
} else {
expect(res.headers.get('x-edge-runtime')).toBe('1');
describe.each(['no-fallback', 'fallback'])('%s', fallbackPath => {
it('should bypass the static cache for a server action when pre-generated', async () => {
const path = `${basePath}/rsc/static/generate-static-params/${fallbackPath}/pre-generated`;
const dynamicPath = `${basePath}/rsc/static/generate-static-params/${fallbackPath}/[slug]`;
const actionId = findActionId(dynamicPath, runtime);

const res = await fetch(
`${ctx.deploymentUrl}${path}`,
generateFormDataPayload(actionId)
);

expect(res.status).toEqual(200);
expect(res.headers.get('x-matched-path')).toBe(
(basePath ? dynamicPath : path) + '.action'
);
expect(res.headers.get('content-type')).toBe('text/x-component');
if (runtime === 'node') {
expect(res.headers.get('x-vercel-cache')).toBe('MISS');
} else {
expect(res.headers.get('x-edge-runtime')).toBe('1');
}
});

// if it's dynamicParams = false we have nothing to
// bypass to
if (fallbackPath !== 'no-fallback') {
it('should bypass the static cache for a server action when not pre-generated', async () => {
const page = `${basePath}/rsc/static/generate-static-params/${fallbackPath}/[slug]`;
const actionId = findActionId(page, runtime);

const res = await fetch(
`${ctx.deploymentUrl}/${basePath}/rsc/static/generate-static-params/${fallbackPath}/not-pre-generated`,
generateFormDataPayload(actionId)
);

expect(res.status).toEqual(200);
expect(res.headers.get('x-matched-path')).toBe(page + '.action');
expect(res.headers.get('content-type')).toBe('text/x-component');
if (runtime === 'node') {
expect(res.headers.get('x-vercel-cache')).toBe('MISS');
} else {
expect(res.headers.get('x-edge-runtime')).toBe('1');
}
});
}
});
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"dependencies": {
"next": "canary",
"react": "19.0.0-rc-f994737d14-20240522",
"react-dom": "19.0.0-rc-f994737d14-20240522"
"react": "19.0.0-rc.0",
"react-dom": "19.0.0-rc.0"
},
"ignoreNextjsUpdates": true
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"dependencies": {
"next": "canary",
"react": "19.0.0-rc-f994737d14-20240522",
"react-dom": "19.0.0-rc-f994737d14-20240522"
"react": "19.0.0-rc.0",
"react-dom": "19.0.0-rc.0"
},
"ignoreNextjsUpdates": true
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"dependencies": {
"next": "canary",
"react": "19.0.0-rc-f994737d14-20240522",
"react-dom": "19.0.0-rc-f994737d14-20240522"
"react": "19.0.0-rc.0",
"react-dom": "19.0.0-rc.0"
},
"ignoreNextjsUpdates": true,
"scripts": {
Expand Down
4 changes: 2 additions & 2 deletions packages/next/test/fixtures/00-app-dir-edge-404/package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"dependencies": {
"next": "canary",
"react": "19.0.0-rc-f994737d14-20240522",
"react-dom": "19.0.0-rc-f994737d14-20240522"
"react": "19.0.0-rc.0",
"react-dom": "19.0.0-rc.0"
},
"ignoreNextjsUpdates": true
}
4 changes: 2 additions & 2 deletions packages/next/test/fixtures/00-app-dir-edge/package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"dependencies": {
"next": "canary",
"react": "19.0.0-rc-f994737d14-20240522",
"react-dom": "19.0.0-rc-f994737d14-20240522"
"react": "19.0.0-rc.0",
"react-dom": "19.0.0-rc.0"
},
"ignoreNextjsUpdates": true
}
4 changes: 2 additions & 2 deletions packages/next/test/fixtures/00-app-dir-i18n/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
},
"dependencies": {
"next": "canary",
"react": "19.0.0-rc-f994737d14-20240522",
"react-dom": "19.0.0-rc-f994737d14-20240522"
"react": "19.0.0-rc.0",
"react-dom": "19.0.0-rc.0"
},
"ignoreNextjsUpdates": true
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"dependencies": {
"next": "canary",
"react": "19.0.0-rc-f994737d14-20240522",
"react-dom": "19.0.0-rc-f994737d14-20240522"
"react": "19.0.0-rc.0",
"react-dom": "19.0.0-rc.0"
},
"ignoreNextjsUpdates": true
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"dependencies": {
"next": "canary",
"react": "19.0.0-rc-f994737d14-20240522",
"react-dom": "19.0.0-rc-f994737d14-20240522"
"react": "19.0.0-rc.0",
"react-dom": "19.0.0-rc.0"
},
"ignoreNextjsUpdates": true
}
4 changes: 2 additions & 2 deletions packages/next/test/fixtures/00-app-dir-no-ppr/package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"dependencies": {
"next": "canary",
"react": "19.0.0-rc-f994737d14-20240522",
"react-dom": "19.0.0-rc-f994737d14-20240522"
"react": "19.0.0-rc.0",
"react-dom": "19.0.0-rc.0"
},
"ignoreNextjsUpdates": true
}
4 changes: 2 additions & 2 deletions packages/next/test/fixtures/00-app-dir-not-found/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
},
"dependencies": {
"next": "canary",
"react": "19.0.0-rc-f994737d14-20240522",
"react-dom": "19.0.0-rc-f994737d14-20240522"
"react": "19.0.0-rc.0",
"react-dom": "19.0.0-rc.0"
},
"devDependencies": {
"@types/node": "20.4.5",
Expand Down
4 changes: 2 additions & 2 deletions packages/next/test/fixtures/00-app-dir-ppr-full/package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"dependencies": {
"next": "canary",
"react": "19.0.0-rc-f994737d14-20240522",
"react-dom": "19.0.0-rc-f994737d14-20240522"
"react": "19.0.0-rc.0",
"react-dom": "19.0.0-rc.0"
},
"ignoreNextjsUpdates": true
}
4 changes: 2 additions & 2 deletions packages/next/test/fixtures/00-app-dir-ppr/package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"dependencies": {
"next": "canary",
"react": "19.0.0-rc-f994737d14-20240522",
"react-dom": "19.0.0-rc-f994737d14-20240522"
"react": "19.0.0-rc.0",
"react-dom": "19.0.0-rc.0"
},
"ignoreNextjsUpdates": true
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"dependencies": {
"next": "canary",
"react": "19.0.0-rc-f994737d14-20240522",
"react-dom": "19.0.0-rc-f994737d14-20240522"
"react": "19.0.0-rc.0",
"react-dom": "19.0.0-rc.0"
},
"ignoreNextjsUpdates": true
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"dependencies": {
"next": "canary",
"react": "19.0.0-rc-f994737d14-20240522",
"react-dom": "19.0.0-rc-f994737d14-20240522"
"react": "19.0.0-rc.0",
"react-dom": "19.0.0-rc.0"
},
"ignoreNextjsUpdates": true
}
4 changes: 2 additions & 2 deletions packages/next/test/fixtures/00-app-dir-static/package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"dependencies": {
"next": "canary",
"react": "19.0.0-rc-f994737d14-20240522",
"react-dom": "19.0.0-rc-f994737d14-20240522"
"react": "19.0.0-rc.0",
"react-dom": "19.0.0-rc.0"
},
"ignoreNextjsUpdates": true
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"dependencies": {
"next": "canary",
"react": "19.0.0-rc-f994737d14-20240522",
"react-dom": "19.0.0-rc-f994737d14-20240522"
"react": "19.0.0-rc.0",
"react-dom": "19.0.0-rc.0"
},
"ignoreNextjsUpdates": true
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"dependencies": {
"next": "canary",
"react": "19.0.0-rc-f994737d14-20240522",
"react-dom": "19.0.0-rc-f994737d14-20240522"
"react": "19.0.0-rc.0",
"react-dom": "19.0.0-rc.0"
},
"ignoreNextjsUpdates": true
}
4 changes: 2 additions & 2 deletions packages/next/test/fixtures/00-build-time-traces/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
},
"dependencies": {
"next": "canary",
"react": "19.0.0-rc-f994737d14-20240522",
"react-dom": "19.0.0-rc-f994737d14-20240522"
"react": "19.0.0-rc.0",
"react-dom": "19.0.0-rc.0"
},
"ignoreNextjsUpdates": true
}
4 changes: 2 additions & 2 deletions packages/next/test/fixtures/00-edge-runtime/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
},
"dependencies": {
"next": "canary",
"react": "19.0.0-rc-f994737d14-20240522",
"react-dom": "19.0.0-rc-f994737d14-20240522"
"react": "19.0.0-rc.0",
"react-dom": "19.0.0-rc.0"
},
"ignoreNextjsUpdates": true
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
},
"dependencies": {
"next": "canary",
"react": "19.0.0-rc-f994737d14-20240522",
"react-dom": "19.0.0-rc-f994737d14-20240522"
"react": "19.0.0-rc.0",
"react-dom": "19.0.0-rc.0"
},
"ignoreNextjsUpdates": true
}

0 comments on commit 84b7445

Please sign in to comment.