Skip to content

Commit 39c0e97

Browse files
authored
Merge branch 'main' into delete-e2e-deploys
2 parents be93b5e + a630ab4 commit 39c0e97

File tree

8 files changed

+94
-7
lines changed

8 files changed

+94
-7
lines changed

cypress/integration/default/appdir.spec.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ describe('appDir', () => {
2929
})
3030
})
3131

32-
it('returns a vary header for RSC data requests to ISR pages', () => {
32+
it.skip('returns a vary header for RSC data requests to ISR pages', () => {
3333
cy.request({
3434
url: '/blog/erica/',
3535
followRedirect: false,
@@ -41,7 +41,7 @@ describe('appDir', () => {
4141
})
4242
})
4343

44-
it('returns a vary header for non-RSC data requests to ISR pages', () => {
44+
it.skip('returns a vary header for non-RSC data requests to ISR pages', () => {
4545
cy.request({
4646
url: '/blog/erica/',
4747
followRedirect: false,

cypress/integration/middleware/standard.spec.ts

+8
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,14 @@ describe('Standard middleware', () => {
3737
cy.getCookie('netlifyCookie').should('have.property', 'value', 'true')
3838
})
3939
})
40+
41+
// https://github.com/netlify/pillar-support/issues/350
42+
it('MiddlewareResponse adds cookies', () => {
43+
cy.request('/cookies/middleware').then((response) => {
44+
cy.getCookie('middlewareCookie').should('have.property', 'value', 'true')
45+
expect(response.headers).to.have.property('x-foo', 'bar')
46+
})
47+
})
4048
})
4149

4250
describe('Middleware matchers', () => {

demos/middleware/middleware.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,13 @@ export async function middleware(req: NextRequest) {
5353
return request.rewrite('/api/hello')
5454
}
5555

56+
if (pathname.startsWith('/cookies/middleware')) {
57+
const response = await new MiddlewareRequest(req).next()
58+
response.cookies.set('middlewareCookie', 'true')
59+
response.headers.set('x-foo', 'bar')
60+
return response
61+
}
62+
5663
if (pathname.startsWith('/cookies')) {
5764
response = NextResponse.next()
5865
response.cookies.set('netlifyCookie', 'true')
@@ -129,8 +136,8 @@ export const config = {
129136
matcher: [
130137
'/api/:all*',
131138
'/headers',
139+
'/cookies/:path*',
132140
{ source: '/static' },
133-
{ source: '/cookies' },
134141
{ source: '/matcher-cookie'},
135142
{ source: '/shows/((?!99|88).*)' },
136143
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
const Cookies = () => {
2+
return (
3+
<div>
4+
<p>The cookie "middlewareCookie" should be set to true</p>
5+
</div>
6+
)
7+
}
8+
9+
export default Cookies

packages/next/src/middleware/response.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,15 @@ export class MiddlewareResponse extends NextResponse {
1111
private readonly dataTransforms: NextDataTransform[]
1212
private readonly elementHandlers: Array<[selector: string, handlers: ElementHandlers]>
1313
constructor(public originResponse: Response) {
14-
super()
14+
// we need to propagate the set-cookie header, so response.cookies.get works correctly
15+
const initHeaders = new Headers()
16+
if (originResponse.headers.has('set-cookie')) {
17+
initHeaders.set('set-cookie', originResponse.headers.get('set-cookie'))
18+
}
19+
20+
super(undefined, {
21+
headers: initHeaders,
22+
})
1523

1624
// These are private in Node when compiling, but we access them in Deno at runtime
1725
Object.defineProperty(this, 'dataTransforms', {

packages/runtime/src/templates/edge-shared/rsc-data.ts

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
import type { EdgeFunction } from 'https://edge.netlify.com'
22

3+
// These are copied from next/dist/build. This file gets copied as part of the next
4+
// runtime build and can't reference the next package directly.
5+
//
6+
// Latest types at https://github.com/vercel/next.js/blob/4a2df3c3752aeddc50fd5ab053440eccf71ae50b/packages/next/src/build/index.ts#L140
37
export declare type SsgRoute = {
48
initialRevalidateSeconds: number | false
59
srcRoute: string | null
6-
dataRoute: string
10+
dataRoute: string | null
711
}
812
export declare type DynamicSsgRoute = {
913
routeRegex: string
1014
fallback: string | null | false
11-
dataRoute: string
15+
dataRoute: string | null
1216
dataRouteRegex: string
1317
}
1418
export declare type PrerenderManifest = {
@@ -35,7 +39,7 @@ const rscifyPath = (route: string) => {
3539
export const getRscDataRouter = ({ routes: staticRoutes, dynamicRoutes }: PrerenderManifest): EdgeFunction => {
3640
const staticRouteSet = new Set(
3741
Object.entries(staticRoutes)
38-
.filter(([, { dataRoute }]) => dataRoute.endsWith('.rsc'))
42+
.filter(([, { dataRoute }]) => dataRoute?.endsWith('.rsc'))
3943
.map(([route]) => route),
4044
)
4145

packages/runtime/src/templates/edge-shared/utils.ts

+11
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,15 @@ export const addMiddlewareHeaders = async (
5757
return response
5858
}
5959

60+
interface ResponseCookies {
61+
readonly _headers: Headers
62+
}
63+
6064
interface MiddlewareResponse extends Response {
6165
originResponse: Response
6266
dataTransforms: NextDataTransform[]
6367
elementHandlers: Array<[selector: string, handlers: ElementHandlers]>
68+
get cookies(): ResponseCookies
6469
}
6570

6671
interface MiddlewareRequest {
@@ -184,6 +189,12 @@ export const buildResponse = async ({
184189
if (request.method === 'HEAD' || request.method === 'OPTIONS') {
185190
return response.originResponse
186191
}
192+
193+
// NextResponse doesn't set cookies onto the originResponse, so we need to copy them over
194+
if (response.cookies._headers.has('set-cookie')) {
195+
response.originResponse.headers.set('set-cookie', response.cookies._headers.get('set-cookie')!)
196+
}
197+
187198
// If it's JSON we don't need to use the rewriter, we can just parse it
188199
if (response.originResponse.headers.get('content-type')?.includes('application/json')) {
189200
const props = await response.originResponse.json()

test/rsc-data.spec.ts

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { getRscDataRouter, PrerenderManifest } from '../packages/runtime/src/templates/edge-shared/rsc-data'
2+
3+
const basePrerenderManifest: PrerenderManifest = {
4+
version: 3,
5+
routes: {},
6+
dynamicRoutes: {},
7+
notFoundRoutes: [],
8+
}
9+
10+
describe('getRscDataRouter', () => {
11+
it('should create a RSC data router when data routes are not present for routes', () => {
12+
const manifest: PrerenderManifest = {
13+
...basePrerenderManifest,
14+
routes: {
15+
'/': {
16+
initialRevalidateSeconds: 1,
17+
srcRoute: null,
18+
dataRoute: '/index.json.rsc',
19+
},
20+
'/api/hello': {
21+
initialRevalidateSeconds: false,
22+
srcRoute: '/api/hello',
23+
dataRoute: null,
24+
},
25+
},
26+
}
27+
28+
let rscDataRouter
29+
30+
// Normally type checking would pick this up, but because this file is copied when generating
31+
// edge functions for the build, we need to make sure it's valid for builds.
32+
//
33+
// See https://github.com/netlify/next-runtime/issues/1940
34+
expect(() => {
35+
rscDataRouter = getRscDataRouter(manifest)
36+
}).not.toThrow()
37+
38+
expect(typeof rscDataRouter).toBe('function')
39+
})
40+
})

0 commit comments

Comments
 (0)