[next]: ensure user rewrites match to action outputs (#11628)

The builder normalizes user rewrites that target pages that have special outputs (`.rsc`, `.prefetch.rsc`). When we added support for `.action` outputs, we need to perform this same normalization to ensure that user rewrites still match. If the rewrite was a greedy match (eg `/:path*`) it'd be ok, but more specific rewrites would have the issue.
This commit is contained in:
Zack Tanner
2024-05-23 12:06:25 -07:00
committed by GitHub
parent 139e8cdb17
commit 5f561f8cfa
4 changed files with 50 additions and 2 deletions

View File

@@ -0,0 +1,5 @@
---
'@vercel/next': patch
---
Ensure user rewrites still match to action outputs

View File

@@ -252,9 +252,19 @@ export async function serverBuild({
for (const rewrite of afterFilesRewrites) {
if (rewrite.src && rewrite.dest) {
// ensures that userland rewrites are still correctly matched to their special outputs
// PPR should match .prefetch.rsc, .rsc, and .action
// non-PPR should match .rsc and .action
// we only add `.action` handling to the regex if flagged on in the build
const rscSuffix = isAppPPREnabled
? `(\\.prefetch)?\\.rsc${hasActionOutputSupport ? '|\\.action' : ''}`
: hasActionOutputSupport
? '(\\.action|\\.rsc)'
: '\\.rsc';
rewrite.src = rewrite.src.replace(
/\/?\(\?:\/\)\?/,
`(?<rscsuff>${isAppPPREnabled ? '(\\.prefetch)?' : ''}\\.rsc)?(?:/)?`
`(?<rscsuff>${rscSuffix})?(?:/)?`
);
let destQueryIndex = rewrite.dest.indexOf('?');

View File

@@ -273,6 +273,26 @@ describe(`${__dirname.split(path.sep).pop()}`, () => {
expect(body).toContain(JSON.stringify(['id', '1', 'd']));
expect(body).not.toContain(JSON.stringify(['id', '1.action', 'd']));
});
it('should work when a rewrite targets an action', async () => {
const targetPath = `${basePath}/rsc/static`;
const canonicalPath = `/rewrite/${basePath}/rsc/static`;
const actionId = findActionId(targetPath, runtime);
const res = await fetch(
`${ctx.deploymentUrl}${canonicalPath}`,
generateFormDataPayload(actionId)
);
expect(res.status).toEqual(200);
expect(res.headers.get('x-matched-path')).toBe(targetPath + '.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('pages', () => {

View File

@@ -1 +1,14 @@
module.exports = {};
module.exports = {
rewrites() {
return [
{
source: '/rewrite/rsc/static',
destination: '/rsc/static',
},
{
source: '/rewrite/edge/rsc/static',
destination: '/edge/rsc/static',
},
];
},
};