FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

fix: notFound preloading bug · evdeveloper/Tanstack-Router-@6b09d62 · GitHub

Commit 6b09d62

Browse files
committed
fix: notFound preloading bug
1 parent 66f596e commit 6b09d62

14 files changed

Lines changed: 1092 additions & 110 deletions

File tree

‎examples/react/vinxi-basic-ssr-streaming/app/middleware.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { defineMiddleware, getContext, setContext } from 'vinxi/server'
1+
import { defineMiddleware, getContext, setContext } from 'vinxi/http'
22

33
export default defineMiddleware({
44
onRequest: (event) => {

‎examples/react/vinxi-basic-ssr-streaming/app/server.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import { renderAsset } from '@vinxi/react'
33
import { Suspense } from 'react'
44
import { PipeableStream, renderToPipeableStream } from 'react-dom/server'
5-
import { eventHandler, setHeader, toWebRequest } from 'vinxi/server'
5+
import { eventHandler, setHeader, toWebRequest } from 'vinxi/http'
66
import {
77
StartServer,
88
transformStreamWithRouter,

‎examples/react/vinxi-basic-ssr/app/middleware.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { defineMiddleware, getContext, setContext } from 'vinxi/server'
1+
import { defineMiddleware, getContext, setContext } from 'vinxi/http'
22

33
export default defineMiddleware({
44
onRequest: (event) => {

‎examples/react/vinxi-basic-ssr/app/server.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import { renderAsset } from '@vinxi/react'
33
import React, { Suspense } from 'react'
44
import { PipeableStream, renderToPipeableStream } from 'react-dom/server'
5-
import { eventHandler, setHeader, toWebRequest } from 'vinxi/server'
5+
import { eventHandler, setHeader, toWebRequest } from 'vinxi/http'
66
import {
77
StartServer,
88
transformStreamWithRouter,

‎examples/react/vinxi-basic/app/middleware.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { defineMiddleware, getContext, setContext } from 'vinxi/server'
1+
import { defineMiddleware, getContext, setContext } from 'vinxi/http'
22

33
export default defineMiddleware({
44
onRequest: (event) => {

‎packages/react-router-server/package.json‎

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,36 @@
3636
"default": "./dist/cjs/server.cjs"
3737
}
3838
},
39+
"./client-runtime": {
40+
"import": {
41+
"types": "./dist/esm/client-runtime.d.ts",
42+
"default": "./dist/esm/client-runtime.js"
43+
},
44+
"require": {
45+
"types": "./dist/cjs/client-runtime.d.cts",
46+
"default": "./dist/cjs/client-runtime.cjs"
47+
}
48+
},
49+
"./server-runtime": {
50+
"import": {
51+
"types": "./dist/esm/server-runtime.d.ts",
52+
"default": "./dist/esm/server-runtime.js"
53+
},
54+
"require": {
55+
"types": "./dist/cjs/server-runtime.d.cts",
56+
"default": "./dist/cjs/server-runtime.cjs"
57+
}
58+
},
59+
"./server-handler": {
60+
"import": {
61+
"types": "./dist/esm/server-handler.d.ts",
62+
"default": "./dist/esm/server-handler.js"
63+
},
64+
"require": {
65+
"types": "./dist/cjs/server-handler.d.cts",
66+
"default": "./dist/cjs/server-handler.cjs"
67+
}
68+
},
3969
"./package.json": "./package.json"
4070
},
4171
"sideEffects": false,
@@ -63,7 +93,8 @@
6393
],
6494
"dependencies": {
6595
"@tanstack/react-cross-context": "workspace:^",
66-
"@tanstack/react-router": "workspace:*"
96+
"@tanstack/react-router": "workspace:*",
97+
"vinxi": "0.2.1"
6798
},
6899
"devDependencies": {
69100
"@vitejs/plugin-react": "^4.2.1",
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import {
2+
CompiledFetcherFnOptions,
3+
FetchFn,
4+
isPlainObject,
5+
} from '@tanstack/react-router'
6+
7+
export function getBaseUrl(base: string | undefined, id: string, name: string) {
8+
return `${base}/_server?_serverId=${encodeURI(id)}&_serverName=${encodeURI(name)}`
9+
}
10+
11+
export function createServerReference<TPayload, TResponse>(
12+
_fn: FetchFn<TPayload, TResponse>,
13+
id: string,
14+
name: string,
15+
) {
16+
let base = getBaseUrl(import.meta.env.SERVER_BASE_URL, id, name)
17+
18+
const proxyFn = (...args: any[]) => handleFetcherArgs(base, args, fetch)
19+
20+
return Object.assign(proxyFn, {
21+
url: base,
22+
})
23+
}
24+
25+
export async function handleFetcherArgs<TPayload>(
26+
base: string,
27+
args: any[],
28+
handler: (request: Request) => Promise<Response>,
29+
) {
30+
const first = args[0]
31+
32+
// If a custom fetcher request is passed, use it
33+
if (isPlainObject(first) && first.method && first.type) {
34+
const opts = first as CompiledFetcherFnOptions<TPayload>
35+
36+
// Arrange the headers
37+
const headers = new Headers({
38+
'server-action-type': opts.type,
39+
...(opts.type === 'payload'
40+
? {
41+
'content-type': 'application/json',
42+
accept: 'application/json',
43+
}
44+
: {}),
45+
...opts.requestInit?.headers,
46+
})
47+
48+
// If the method is GET, we need to move the payload to the query string
49+
if (opts.method === 'GET') {
50+
// If the method is GET, we need to move the payload to the query string
51+
const query = new URLSearchParams(opts.payload as any)
52+
base += `&${query.toString()}`
53+
}
54+
55+
// Create the request
56+
const request = new Request(base, {
57+
...opts.requestInit,
58+
headers,
59+
...(opts.method === 'POST' && opts.payload
60+
? { body: JSON.stringify(opts.payload) }
61+
: {}),
62+
})
63+
64+
// Fetch it
65+
const response = await handler(request)
66+
67+
// After handling, return the response itself
68+
return handleResponse(response)
69+
}
70+
71+
// If not a custom fetcher, just proxy the arguments
72+
// through as a POST request
73+
const request = new Request(base, {
74+
method: 'POST',
75+
headers: {
76+
Accept: 'application/json',
77+
'Content-Type': 'application/json',
78+
'server-action-type': 'args',
79+
},
80+
body: JSON.stringify(args),
81+
})
82+
83+
const result = handleResponse(await handler(request))
84+
85+
// If the response is JSON, return it parsed
86+
const contentType = result.headers.get('content-type')
87+
if (contentType && contentType.includes('application/json')) {
88+
return result.json()
89+
} else {
90+
// Otherwise, return the text as a fallback
91+
// If the user wants more than this, they can pass a
92+
// request instead
93+
return result.text()
94+
}
95+
}
96+
97+
function handleResponse(response: Response) {
98+
if (!response.ok) {
99+
const err = new Error(`HTTP error! status: ${response.status}`)
100+
throw Object.assign(err, { response })
101+
}
102+
103+
return response
104+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/// <reference types="vinxi/types/server" />
2+
import { isNotFound, isRedirect } from '@tanstack/react-router'
3+
import { eventHandler, toWebRequest } from 'vinxi/http'
4+
import invariant from 'vinxi/lib/invariant'
5+
import { getManifest } from 'vinxi/manifest'
6+
7+
export default eventHandler(handleServerAction)
8+
9+
export async function handleServerAction(event: any) {
10+
const request = toWebRequest(event) as Request
11+
return await handleServerRequest(request)
12+
}
13+
14+
export async function handleServerRequest(request: Request) {
15+
const method = request.method
16+
const url = new URL(request.url, 'http://localhost')
17+
const search = new URLSearchParams(url.search)
18+
19+
const serverId = search.get('_serverId')
20+
21+
if (serverId) {
22+
invariant(typeof serverId === 'string', 'Invalid server action')
23+
24+
// This is the client-side case
25+
const [filepath, name] = serverId.split('#')
26+
27+
const action = (
28+
await getManifest(import.meta.env.ROUTER_NAME).chunks[filepath].import()
29+
)[name] as Function
30+
31+
// If the request is a
32+
33+
try {
34+
const args = await (async () => {
35+
if (request.headers.get('server-action-type') === 'payload') {
36+
return [await request.json(), { method, request }] as const
37+
}
38+
39+
if (request.headers.get('server-action-type') === 'request') {
40+
return [request, { method, request }] as const
41+
}
42+
43+
// if (request.headers.get('server-action-type') === 'args') {
44+
return (await request.json()) as any[]
45+
// }
46+
})()
47+
48+
const response = await action.call(null, args)
49+
50+
if (response instanceof Response) {
51+
return response
52+
}
53+
54+
return new Response(JSON.stringify(response ?? null), {
55+
status: 200,
56+
headers: {
57+
'Content-Type': 'application/json',
58+
},
59+
})
60+
} catch (error: any) {
61+
// Currently this server-side context has no idea how to
62+
// build final URLs, so we need to defer that to the client.
63+
// The client will check for __redirect and __notFound keys,
64+
// and if they exist, it will handle them appropriately.
65+
66+
if (isRedirect(error)) {
67+
// TODO: Use a common variable for the __redirect key
68+
return new Response(JSON.stringify({ __redirect: error }))
69+
}
70+
if (isNotFound(error)) {
71+
// TODO: Use a common variable for the __notFound key
72+
return new Response(JSON.stringify({ __notFound: error }))
73+
}
74+
75+
console.error(error)
76+
return new Response(JSON.stringify({ error: error.message }), {
77+
status: 500,
78+
headers: {
79+
'Content-Type': 'application/json',
80+
'server-function': 'error',
81+
},
82+
})
83+
}
84+
} else {
85+
throw new Error('Invalid request')
86+
}
87+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { FetchFn } from '@tanstack/react-router'
2+
import { getBaseUrl, handleFetcherArgs } from './client-runtime'
3+
import { handleServerRequest } from './server-handler'
4+
5+
export function createServerReference<TPayload, TResponse>(
6+
_fn: FetchFn<TPayload, TResponse>,
7+
id: string,
8+
name: string,
9+
) {
10+
let base = getBaseUrl(import.meta.env.SERVER_BASE_URL, id, name)
11+
12+
const proxyFn = (...args: any[]) =>
13+
handleFetcherArgs(base, args, handleServerRequest)
14+
15+
return Object.assign(proxyFn, {
16+
url: base,
17+
})
18+
}

‎packages/react-router-server/vite.config.ts‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,13 @@ const config = defineConfig({
99
export default mergeConfig(
1010
config,
1111
tanstackBuildConfig({
12-
entry: ['./src/client.tsx', './src/server.tsx'],
12+
entry: [
13+
'./src/client.tsx',
14+
'./src/server.tsx',
15+
'./src/client-runtime.tsx',
16+
'./src/server-runtime.tsx',
17+
'./src/server-handler.tsx',
18+
],
1319
srcDir: './src',
1420
}),
1521
)

0 commit comments

Comments
 (0)

Back | FazBrowse Home | New Git URL