File tree Expand file tree Collapse file tree 4 files changed +39
-15
lines changed Expand file tree Collapse file tree 4 files changed +39
-15
lines changed Original file line number Diff line number Diff line change 1
- import { test } from '@playwright/test'
2
- import { setupInlineFixture , useFixture } from './fixture'
1
+ import { expect , test } from '@playwright/test'
2
+ import { setupInlineFixture , useFixture , type Fixture } from './fixture'
3
3
import { defineStarterTest } from './starter'
4
4
5
5
test . describe ( ( ) => {
@@ -27,17 +27,31 @@ test.describe(() => {
27
27
28
28
test . describe ( 'dev-base' , ( ) => {
29
29
const f = useFixture ( { root, mode : 'dev' } )
30
- defineStarterTest ( {
30
+ const f2 : Fixture = {
31
31
...f ,
32
32
url : ( url ) => new URL ( url ?? './' , f . url ( './custom-base/' ) ) . href ,
33
- } )
33
+ }
34
+ defineStarterTest ( f2 )
35
+ testRequestUrl ( f2 )
34
36
} )
35
37
36
38
test . describe ( 'build-base' , ( ) => {
37
39
const f = useFixture ( { root, mode : 'build' } )
38
- defineStarterTest ( {
40
+ const f2 : Fixture = {
39
41
...f ,
40
42
url : ( url ) => new URL ( url ?? './' , f . url ( './custom-base/' ) ) . href ,
41
- } )
43
+ }
44
+ defineStarterTest ( f2 )
45
+ testRequestUrl ( f2 )
42
46
} )
47
+
48
+ function testRequestUrl ( f : Fixture ) {
49
+ test ( 'request url' , async ( { page } ) => {
50
+ await page . goto ( f . url ( ) )
51
+ await page . waitForSelector ( '#root' )
52
+ await expect ( page . locator ( '.card' ) . nth ( 2 ) ) . toHaveText (
53
+ `Request URL: ${ f . url ( ) } ` ,
54
+ )
55
+ } )
56
+ }
43
57
} )
Original file line number Diff line number Diff line change @@ -58,14 +58,18 @@ export default async function handler(request: Request): Promise<Response> {
58
58
// we render RSC stream after handling server function request
59
59
// so that new render reflects updated state from server function call
60
60
// to achieve single round trip to mutate and fetch from server.
61
- const rscPayload : RscPayload = { root : < Root /> , formState, returnValue }
61
+ const url = new URL ( request . url )
62
+ const rscPayload : RscPayload = {
63
+ root : < Root url = { url } /> ,
64
+ formState,
65
+ returnValue,
66
+ }
62
67
const rscOptions = { temporaryReferences }
63
68
const rscStream = renderToReadableStream < RscPayload > ( rscPayload , rscOptions )
64
69
65
70
// respond RSC stream without HTML rendering based on framework's convention.
66
71
// here we use request header `content-type`.
67
72
// additionally we allow `?__rsc` and `?__html` to easily view payload directly.
68
- const url = new URL ( request . url )
69
73
const isRscRequest =
70
74
( ! request . headers . get ( 'accept' ) ?. includes ( 'text/html' ) &&
71
75
! url . searchParams . has ( '__html' ) ) ||
Original file line number Diff line number Diff line change @@ -4,7 +4,7 @@ import { getServerCounter, updateServerCounter } from './action.tsx'
4
4
import reactLogo from './assets/react.svg'
5
5
import { ClientCounter } from './client.tsx'
6
6
7
- export function Root ( ) {
7
+ export function Root ( props : { url : URL } ) {
8
8
return (
9
9
< html lang = "en" >
10
10
< head >
@@ -14,13 +14,13 @@ export function Root() {
14
14
< title > Vite + RSC</ title >
15
15
</ head >
16
16
< body >
17
- < App />
17
+ < App { ... props } />
18
18
</ body >
19
19
</ html >
20
20
)
21
21
}
22
22
23
- function App ( ) {
23
+ function App ( props : { url : URL } ) {
24
24
return (
25
25
< div id = "root" >
26
26
< div >
@@ -43,6 +43,7 @@ function App() {
43
43
< button > Server Counter: { getServerCounter ( ) } </ button >
44
44
</ form >
45
45
</ div >
46
+ < div className = "card" > Request URL: { props . url ?. href } </ div >
46
47
< ul className = "read-the-docs" >
47
48
< li >
48
49
Edit < code > src/client.tsx</ code > to test client HMR.
@@ -52,15 +53,15 @@ function App() {
52
53
</ li >
53
54
< li >
54
55
Visit{ ' ' }
55
- < a href = "/ ?__rsc" target = "_blank" >
56
- < code > / ?__rsc</ code >
56
+ < a href = "?__rsc" target = "_blank" >
57
+ < code > ?__rsc</ code >
57
58
</ a > { ' ' }
58
59
to view RSC stream payload.
59
60
</ li >
60
61
< li >
61
62
Visit{ ' ' }
62
- < a href = "/ ?__nojs" target = "_blank" >
63
- < code > / ?__nojs</ code >
63
+ < a href = "?__nojs" target = "_blank" >
64
+ < code > ?__nojs</ code >
64
65
</ a > { ' ' }
65
66
to test server action without js enabled.
66
67
</ li >
Original file line number Diff line number Diff line change @@ -471,6 +471,10 @@ export default function vitePluginRsc(
471
471
`[vite-rsc] failed to resolve server handler '${ source } '` ,
472
472
)
473
473
const mod = await environment . runner . import ( resolved . id )
474
+ // expose original request url to server handler.
475
+ // for example, this restores `base` which is automatically stripped by Vite.
476
+ // https://github.com/vitejs/vite/blob/84079a84ad94de4c1ef4f1bdb2ab448ff2c01196/packages/vite/src/node/server/middlewares/base.ts#L18-L20
477
+ req . url = req . originalUrl ?? req . url
474
478
// ensure catching rejected promise
475
479
// https://github.com/mjackson/remix-the-web/blob/b5aa2ae24558f5d926af576482caf6e9b35461dc/packages/node-fetch-server/src/lib/request-listener.ts#L87
476
480
await createRequestListener ( mod . default ) ( req , res )
@@ -506,6 +510,7 @@ export default function vitePluginRsc(
506
510
return ( ) => {
507
511
server . middlewares . use ( async ( req , res , next ) => {
508
512
try {
513
+ req . url = req . originalUrl ?? req . url
509
514
await handler ( req , res )
510
515
} catch ( e ) {
511
516
next ( e )
You can’t perform that action at this time.
0 commit comments