@mcansh/react-router-fastify 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +435 -0
- package/LICENSE +21 -0
- package/README.md +260 -0
- package/dist/index.d.mts +82 -0
- package/dist/index.mjs +182 -0
- package/dist/index.mjs.map +1 -0
- package/dist/vite-runtime-Cuj_Fjkd.mjs +18 -0
- package/dist/vite-runtime-Cuj_Fjkd.mjs.map +1 -0
- package/dist/vite.d.mts +33 -0
- package/dist/vite.mjs +181 -0
- package/dist/vite.mjs.map +1 -0
- package/package.json +87 -0
- package/src/fastify.ts +131 -0
- package/src/handler.ts +52 -0
- package/src/index.ts +13 -0
- package/src/request.ts +94 -0
- package/src/response.ts +65 -0
- package/src/vite-runtime.ts +27 -0
- package/src/vite.ts +272 -0
package/README.md
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
# react-router-fastify
|
|
2
|
+
|
|
3
|
+
A Fastify adapter for React Router v8 framework mode, plus a Vite plugin so `react-router dev` runs through your Fastify server.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Adapts Fastify requests and replies to React Router's Web Fetch request/response runtime
|
|
8
|
+
- Registers React Router as a Fastify catch-all route while preserving your own Fastify routes
|
|
9
|
+
- Loads `virtual:react-router/server-build` during development
|
|
10
|
+
- Serves `build/client` with `@fastify/static` in production
|
|
11
|
+
- Provides a Vite plugin that mounts your Fastify app during `react-router dev`
|
|
12
|
+
- Preserves shared server/app module identity for React Router context tokens
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
npm i @mcansh/react-router-fastify fastify react-router @react-router/node
|
|
18
|
+
npm i -D @react-router/dev vite
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
React Router v8 currently requires Node `>=22.22.0`.
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
Create a server module that exports a factory. The factory receives the Vite dev server in development and `undefined` in production.
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
// server.ts
|
|
29
|
+
import { pathToFileURL } from "node:url"
|
|
30
|
+
|
|
31
|
+
import { fastifyReactRouter } from "@mcansh/react-router-fastify"
|
|
32
|
+
import { fastify } from "fastify"
|
|
33
|
+
import type { ViteDevServer } from "vite"
|
|
34
|
+
|
|
35
|
+
export async function createServer(vite?: ViteDevServer) {
|
|
36
|
+
let app = fastify()
|
|
37
|
+
|
|
38
|
+
app.get("/api/health", async () => ({ ok: true }))
|
|
39
|
+
|
|
40
|
+
await app.register(fastifyReactRouter, { devServer: vite })
|
|
41
|
+
|
|
42
|
+
return app
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
let isMain = import.meta.url === pathToFileURL(process.argv[1]).href
|
|
46
|
+
if (isMain) {
|
|
47
|
+
let app = await createServer()
|
|
48
|
+
await app.listen({ port: 3000, host: "0.0.0.0" })
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Add the Vite plugin so `react-router dev` uses that Fastify server:
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
// vite.config.ts
|
|
56
|
+
import { reactRouter } from "@react-router/dev/vite"
|
|
57
|
+
import { fastifyReactRouterDev } from "@mcansh/react-router-fastify/vite"
|
|
58
|
+
import { defineConfig } from "vite"
|
|
59
|
+
|
|
60
|
+
export default defineConfig({
|
|
61
|
+
plugins: [reactRouter(), fastifyReactRouterDev({ entry: "./server.ts" })],
|
|
62
|
+
})
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Use the normal React Router commands:
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"scripts": {
|
|
70
|
+
"dev": "react-router dev",
|
|
71
|
+
"build": "react-router build",
|
|
72
|
+
"start": "NODE_ENV=production node ./server.js"
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Example
|
|
78
|
+
|
|
79
|
+
A runnable example lives in `examples/basic`:
|
|
80
|
+
|
|
81
|
+
```sh
|
|
82
|
+
pnpm install
|
|
83
|
+
pnpm run example:dev
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
The example includes a Fastify API route at `/api/health`, `getLoadContext`
|
|
87
|
+
that seeds React Router context, root route middleware that updates that same
|
|
88
|
+
context, and a `vite.config.ts` that uses `fastifyReactRouterDev` with the
|
|
89
|
+
standard `react-router dev` command.
|
|
90
|
+
|
|
91
|
+
## Shared Context
|
|
92
|
+
|
|
93
|
+
React Router v8 uses `RouterContextProvider` for loaders, actions, and
|
|
94
|
+
middleware. When your Fastify server and React Router app both need the same
|
|
95
|
+
context token, put that token in a module that both sides import through the
|
|
96
|
+
same package import specifier.
|
|
97
|
+
|
|
98
|
+
Add a package import for the shared module:
|
|
99
|
+
|
|
100
|
+
```json
|
|
101
|
+
{
|
|
102
|
+
"imports": {
|
|
103
|
+
"#request-info": "./app/request-info.ts"
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Create the context token in your app:
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
// app/request-info.ts
|
|
112
|
+
import { createContext } from "react-router"
|
|
113
|
+
|
|
114
|
+
export interface RequestInfo {
|
|
115
|
+
requestId: string
|
|
116
|
+
userAgent: string
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export let requestInfoContext = createContext<RequestInfo>()
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Seed it from `getLoadContext`:
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
// server.ts
|
|
126
|
+
import { requestInfoContext } from "#request-info"
|
|
127
|
+
import { fastifyReactRouter } from "@mcansh/react-router-fastify"
|
|
128
|
+
import { RouterContextProvider } from "react-router"
|
|
129
|
+
|
|
130
|
+
app.register(fastifyReactRouter, {
|
|
131
|
+
devServer: vite,
|
|
132
|
+
getLoadContext(request) {
|
|
133
|
+
let context = new RouterContextProvider()
|
|
134
|
+
context.set(requestInfoContext, {
|
|
135
|
+
requestId: request.id,
|
|
136
|
+
userAgent: request.headers["user-agent"] ?? "unknown",
|
|
137
|
+
})
|
|
138
|
+
return context
|
|
139
|
+
},
|
|
140
|
+
})
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Read or update the same context from React Router middleware and route modules:
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
// app/root.tsx
|
|
147
|
+
import { requestInfoContext } from "#request-info"
|
|
148
|
+
import type { MiddlewareFunction } from "react-router"
|
|
149
|
+
|
|
150
|
+
export const middleware: MiddlewareFunction[] = [
|
|
151
|
+
async ({ context }, next) => {
|
|
152
|
+
let requestInfo = context.get(requestInfoContext)
|
|
153
|
+
context.set(requestInfoContext, requestInfo)
|
|
154
|
+
return next()
|
|
155
|
+
},
|
|
156
|
+
]
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
// app/routes/home.tsx
|
|
161
|
+
import { requestInfoContext } from "#request-info"
|
|
162
|
+
import type { LoaderFunctionArgs } from "react-router"
|
|
163
|
+
|
|
164
|
+
export async function loader({ context }: LoaderFunctionArgs) {
|
|
165
|
+
return {
|
|
166
|
+
requestInfo: context.get(requestInfoContext),
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Tell the Vite plugin to keep that package import external in React Router's SSR
|
|
172
|
+
build:
|
|
173
|
+
|
|
174
|
+
```ts
|
|
175
|
+
// vite.config.ts
|
|
176
|
+
import { reactRouter } from "@react-router/dev/vite"
|
|
177
|
+
import { fastifyReactRouterDev } from "@mcansh/react-router-fastify/vite"
|
|
178
|
+
import { defineConfig } from "vite"
|
|
179
|
+
|
|
180
|
+
export default defineConfig({
|
|
181
|
+
plugins: [
|
|
182
|
+
reactRouter(),
|
|
183
|
+
fastifyReactRouterDev({
|
|
184
|
+
entry: "./server.ts",
|
|
185
|
+
externalizeServerEntryImports: ["#request-info"],
|
|
186
|
+
}),
|
|
187
|
+
],
|
|
188
|
+
})
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
This prevents the production server bundle from inlining a second
|
|
192
|
+
`createContext()` instance. The built React Router server keeps
|
|
193
|
+
`import { requestInfoContext } from "#request-info"`, so `server.ts`,
|
|
194
|
+
middleware, and loaders all use the same token.
|
|
195
|
+
|
|
196
|
+
## Production
|
|
197
|
+
|
|
198
|
+
Without a Vite dev server, `fastifyReactRouter` imports `build/server/index.js` and serves static files from `build/client`:
|
|
199
|
+
|
|
200
|
+
```sh
|
|
201
|
+
react-router build
|
|
202
|
+
NODE_ENV=production node ./server.js
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
If your build output uses different paths, pass them directly:
|
|
206
|
+
|
|
207
|
+
```ts
|
|
208
|
+
await app.register(fastifyReactRouter, {
|
|
209
|
+
serverBuildPath: "dist/server.js",
|
|
210
|
+
clientBuildDirectory: "dist/client",
|
|
211
|
+
})
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## API
|
|
215
|
+
|
|
216
|
+
`fastifyReactRouter` options:
|
|
217
|
+
|
|
218
|
+
- `devServer` - Vite dev server, provided by `fastifyReactRouterDev` during development
|
|
219
|
+
- `basePath` - URL base path for static files and the catch-all route, default `/`
|
|
220
|
+
- `serverBuildPath` - production server build module, default `build/server/index.js`
|
|
221
|
+
- `clientBuildDirectory` - production client asset directory, default `build/client`
|
|
222
|
+
- `mode` - value passed to React Router's request handler, default `process.env.NODE_ENV`
|
|
223
|
+
- `getLoadContext` - returns a `RouterContextProvider` for each request
|
|
224
|
+
- `build` - production-only React Router server build or build loader override
|
|
225
|
+
- `staticOptions` - options forwarded to `@fastify/static`
|
|
226
|
+
- `assetCacheControl` - cache-control string for files under `<clientBuildDirectory>/assets`
|
|
227
|
+
- `fileCacheControl` - cache-control string for other files in `clientBuildDirectory`
|
|
228
|
+
- `routeOptions` - Fastify route options for the catch-all route
|
|
229
|
+
|
|
230
|
+
When `devServer` is provided, `fastifyReactRouter` always uses Vite's
|
|
231
|
+
`virtual:react-router/server-build` module. This keeps development requests
|
|
232
|
+
hot-reloaded even if a production `build` override is configured.
|
|
233
|
+
|
|
234
|
+
`fastifyReactRouterDev` options:
|
|
235
|
+
|
|
236
|
+
- `entry` - server module loaded by Vite, default `./server.ts`
|
|
237
|
+
- `exportName` - named server factory export, default `createServer`
|
|
238
|
+
- `externalizeServerEntryImports` - keeps local `#` and relative imports from
|
|
239
|
+
the server entry external in the React Router SSR build; pass an explicit
|
|
240
|
+
list such as `["#request-info"]` for tighter production packaging control.
|
|
241
|
+
Externalized modules must be available to Node in production.
|
|
242
|
+
|
|
243
|
+
## Lower-level Handler
|
|
244
|
+
|
|
245
|
+
You can wire the route yourself with `createRequestHandler`:
|
|
246
|
+
|
|
247
|
+
```ts
|
|
248
|
+
import { createRequestHandler } from "@mcansh/react-router-fastify"
|
|
249
|
+
|
|
250
|
+
app.all(
|
|
251
|
+
"*",
|
|
252
|
+
createRequestHandler({
|
|
253
|
+
build: () => import("./build/server/index.js"),
|
|
254
|
+
}),
|
|
255
|
+
)
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
## License
|
|
259
|
+
|
|
260
|
+
See [LICENSE](https://github.com/mcansh/remix-fastify/blob/main/LICENSE)
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { FastifyStaticOptions } from "@fastify/static";
|
|
2
|
+
import { RouterContextProvider, ServerBuild } from "react-router";
|
|
3
|
+
import { FastifyInstance, FastifyReply, FastifyRequest, RouteShorthandOptions } from "fastify";
|
|
4
|
+
import { ViteDevServer } from "vite";
|
|
5
|
+
|
|
6
|
+
//#region src/handler.d.ts
|
|
7
|
+
type HttpRequest = FastifyRequest["raw"];
|
|
8
|
+
type HttpResponse = FastifyReply["raw"];
|
|
9
|
+
type ReactRouterLoadContext = RouterContextProvider;
|
|
10
|
+
type GetLoadContextFunction = (request: FastifyRequest, reply: FastifyReply) => ReactRouterLoadContext | undefined | Promise<ReactRouterLoadContext | undefined>;
|
|
11
|
+
type RequestHandler = (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
|
|
12
|
+
interface CreateRequestHandlerOptions {
|
|
13
|
+
build: ServerBuild | (() => ServerBuild | Promise<ServerBuild>);
|
|
14
|
+
getLoadContext?: GetLoadContextFunction;
|
|
15
|
+
mode?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Creates a Fastify route handler backed by React Router's server runtime.
|
|
19
|
+
*
|
|
20
|
+
* @param options React Router build, mode, and optional load context hook.
|
|
21
|
+
* @returns Fastify route handler.
|
|
22
|
+
*/
|
|
23
|
+
declare function createRequestHandler(options: CreateRequestHandlerOptions): RequestHandler;
|
|
24
|
+
//#endregion
|
|
25
|
+
//#region src/fastify.d.ts
|
|
26
|
+
interface FastifyReactRouterOptions {
|
|
27
|
+
devServer?: ViteDevServer;
|
|
28
|
+
basePath?: string;
|
|
29
|
+
serverBuildPath?: string;
|
|
30
|
+
clientBuildDirectory?: string;
|
|
31
|
+
mode?: string;
|
|
32
|
+
getLoadContext?: GetLoadContextFunction;
|
|
33
|
+
build?: ServerBuild | (() => ServerBuild | Promise<ServerBuild>);
|
|
34
|
+
staticOptions?: FastifyStaticOptions;
|
|
35
|
+
assetCacheControl?: string;
|
|
36
|
+
fileCacheControl?: string;
|
|
37
|
+
routeOptions?: RouteShorthandOptions;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Fastify plugin that serves React Router framework builds.
|
|
41
|
+
*
|
|
42
|
+
* @param fastify Fastify instance.
|
|
43
|
+
* @param options Adapter options.
|
|
44
|
+
*/
|
|
45
|
+
declare function fastifyReactRouter(fastify: FastifyInstance, options: FastifyReactRouterOptions): Promise<void>;
|
|
46
|
+
//#endregion
|
|
47
|
+
//#region src/request.d.ts
|
|
48
|
+
/**
|
|
49
|
+
* Copies Fastify's normalized request headers into Web Fetch `Headers`.
|
|
50
|
+
*
|
|
51
|
+
* @param source Fastify request headers.
|
|
52
|
+
* @returns Fetch-compatible headers.
|
|
53
|
+
*/
|
|
54
|
+
declare function createHeaders(source: FastifyRequest["headers"]): Headers;
|
|
55
|
+
/**
|
|
56
|
+
* Builds the absolute request URL that React Router expects.
|
|
57
|
+
*
|
|
58
|
+
* @param request Fastify request.
|
|
59
|
+
* @returns Absolute URL for the original incoming request.
|
|
60
|
+
*/
|
|
61
|
+
declare function createUrl(request: FastifyRequest): string;
|
|
62
|
+
/**
|
|
63
|
+
* Adapts a Fastify request to a Web Fetch `Request`.
|
|
64
|
+
*
|
|
65
|
+
* @param request Fastify request.
|
|
66
|
+
* @param reply Fastify reply, used to abort work when the connection closes.
|
|
67
|
+
* @returns Fetch-compatible request for React Router.
|
|
68
|
+
*/
|
|
69
|
+
declare function createRequest(request: FastifyRequest, reply: FastifyReply): Request;
|
|
70
|
+
//#endregion
|
|
71
|
+
//#region src/response.d.ts
|
|
72
|
+
/**
|
|
73
|
+
* Writes a Web Fetch `Response` through a Fastify reply.
|
|
74
|
+
*
|
|
75
|
+
* @param reply Fastify reply.
|
|
76
|
+
* @param response React Router response.
|
|
77
|
+
* @returns A promise that settles after the response is sent.
|
|
78
|
+
*/
|
|
79
|
+
declare function sendResponse(reply: FastifyReply, response: Response): Promise<void>;
|
|
80
|
+
//#endregion
|
|
81
|
+
export { type CreateRequestHandlerOptions, type FastifyReactRouterOptions, type GetLoadContextFunction, type HttpRequest, type HttpResponse, type ReactRouterLoadContext, type RequestHandler, createHeaders, createRequest, createRequestHandler, createUrl, fastifyReactRouter, sendResponse };
|
|
82
|
+
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { t as importSsrModule } from "./vite-runtime-Cuj_Fjkd.mjs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
import fastifyStatic from "@fastify/static";
|
|
5
|
+
import { createRequestHandler as createRequestHandler$1 } from "react-router";
|
|
6
|
+
import { Readable } from "node:stream";
|
|
7
|
+
import { createReadableStreamFromReadable } from "@react-router/node";
|
|
8
|
+
//#region src/request.ts
|
|
9
|
+
/**
|
|
10
|
+
* Copies Fastify's normalized request headers into Web Fetch `Headers`.
|
|
11
|
+
*
|
|
12
|
+
* @param source Fastify request headers.
|
|
13
|
+
* @returns Fetch-compatible headers.
|
|
14
|
+
*/
|
|
15
|
+
function createHeaders(source) {
|
|
16
|
+
let headers = new Headers();
|
|
17
|
+
for (let [name, value] of Object.entries(source)) {
|
|
18
|
+
if (value == null) continue;
|
|
19
|
+
if (Array.isArray(value)) for (let item of value) headers.append(name, item);
|
|
20
|
+
else headers.set(name, value);
|
|
21
|
+
}
|
|
22
|
+
return headers;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Builds the absolute request URL that React Router expects.
|
|
26
|
+
*
|
|
27
|
+
* @param request Fastify request.
|
|
28
|
+
* @returns Absolute URL for the original incoming request.
|
|
29
|
+
*/
|
|
30
|
+
function createUrl(request) {
|
|
31
|
+
return `${request.protocol}://${request.host}${request.originalUrl}`;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Adapts a Fastify request to a Web Fetch `Request`.
|
|
35
|
+
*
|
|
36
|
+
* @param request Fastify request.
|
|
37
|
+
* @param reply Fastify reply, used to abort work when the connection closes.
|
|
38
|
+
* @returns Fetch-compatible request for React Router.
|
|
39
|
+
*/
|
|
40
|
+
function createRequest(request, reply) {
|
|
41
|
+
let controller = new AbortController();
|
|
42
|
+
let init = {
|
|
43
|
+
method: request.method,
|
|
44
|
+
headers: createHeaders(request.headers),
|
|
45
|
+
signal: controller.signal
|
|
46
|
+
};
|
|
47
|
+
reply.raw.once("finish", () => {
|
|
48
|
+
controller = null;
|
|
49
|
+
});
|
|
50
|
+
reply.raw.once("close", () => {
|
|
51
|
+
controller?.abort();
|
|
52
|
+
});
|
|
53
|
+
if (["GET", "HEAD"].includes(request.method) === false) {
|
|
54
|
+
init.body = getBody(request);
|
|
55
|
+
init.duplex = "half";
|
|
56
|
+
}
|
|
57
|
+
return new Request(createUrl(request), init);
|
|
58
|
+
}
|
|
59
|
+
function getBody(request) {
|
|
60
|
+
let body = request.body;
|
|
61
|
+
if (body == null) return createReadableStreamFromReadable(request.raw);
|
|
62
|
+
if (body instanceof Readable) return createReadableStreamFromReadable(body);
|
|
63
|
+
if (body instanceof ReadableStream) return body;
|
|
64
|
+
if (body instanceof URLSearchParams) return body;
|
|
65
|
+
if (body instanceof ArrayBuffer) return body;
|
|
66
|
+
if (body instanceof Blob) return body;
|
|
67
|
+
if (body instanceof FormData) return body;
|
|
68
|
+
if (ArrayBuffer.isView(body)) return new Uint8Array(body.buffer, body.byteOffset, body.byteLength);
|
|
69
|
+
if (typeof body === "string") return body;
|
|
70
|
+
return JSON.stringify(body);
|
|
71
|
+
}
|
|
72
|
+
//#endregion
|
|
73
|
+
//#region src/response.ts
|
|
74
|
+
/**
|
|
75
|
+
* Writes a Web Fetch `Response` through a Fastify reply.
|
|
76
|
+
*
|
|
77
|
+
* @param reply Fastify reply.
|
|
78
|
+
* @param response React Router response.
|
|
79
|
+
* @returns A promise that settles after the response is sent.
|
|
80
|
+
*/
|
|
81
|
+
async function sendResponse(reply, response) {
|
|
82
|
+
reply.status(response.status);
|
|
83
|
+
writeHeaders(reply, response.headers);
|
|
84
|
+
if (response.body == null) return reply.send();
|
|
85
|
+
return reply.send(readableFromWeb(response.body));
|
|
86
|
+
}
|
|
87
|
+
function writeHeaders(reply, headers) {
|
|
88
|
+
let cookies = readSetCookies(headers);
|
|
89
|
+
for (let [name, value] of headers) {
|
|
90
|
+
if (name.toLowerCase() === "set-cookie" && cookies.length > 0) continue;
|
|
91
|
+
reply.header(name, value);
|
|
92
|
+
}
|
|
93
|
+
if (cookies.length > 0) reply.header("set-cookie", cookies);
|
|
94
|
+
}
|
|
95
|
+
function readSetCookies(headers) {
|
|
96
|
+
let cookies = headers.getSetCookie?.();
|
|
97
|
+
if (cookies && cookies.length > 0) return cookies;
|
|
98
|
+
let cookie = headers.get("Set-Cookie");
|
|
99
|
+
return cookie ? [cookie] : [];
|
|
100
|
+
}
|
|
101
|
+
function readableFromWeb(body) {
|
|
102
|
+
let reader = body.getReader();
|
|
103
|
+
return new Readable({ read() {
|
|
104
|
+
reader.read().then(({ done, value }) => {
|
|
105
|
+
this.push(done ? null : Buffer.from(value));
|
|
106
|
+
}, (error) => {
|
|
107
|
+
this.destroy(error instanceof Error ? error : new Error(String(error)));
|
|
108
|
+
});
|
|
109
|
+
} });
|
|
110
|
+
}
|
|
111
|
+
//#endregion
|
|
112
|
+
//#region src/handler.ts
|
|
113
|
+
/**
|
|
114
|
+
* Creates a Fastify route handler backed by React Router's server runtime.
|
|
115
|
+
*
|
|
116
|
+
* @param options React Router build, mode, and optional load context hook.
|
|
117
|
+
* @returns Fastify route handler.
|
|
118
|
+
*/
|
|
119
|
+
function createRequestHandler(options) {
|
|
120
|
+
let reactRouterHandler = createRequestHandler$1(options.build, options.mode ?? process.env.NODE_ENV);
|
|
121
|
+
return async (request, reply) => {
|
|
122
|
+
await sendResponse(reply, await reactRouterHandler(createRequest(request, reply), await options.getLoadContext?.(request, reply)));
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
//#endregion
|
|
126
|
+
//#region src/fastify.ts
|
|
127
|
+
/**
|
|
128
|
+
* Fastify plugin that serves React Router framework builds.
|
|
129
|
+
*
|
|
130
|
+
* @param fastify Fastify instance.
|
|
131
|
+
* @param options Adapter options.
|
|
132
|
+
*/
|
|
133
|
+
async function fastifyReactRouter(fastify, options) {
|
|
134
|
+
let { devServer, basePath = "/", serverBuildPath = "build/server/index.js", clientBuildDirectory = "build/client", mode = process.env.NODE_ENV, getLoadContext, build, staticOptions, assetCacheControl = "public, max-age=31536000, immutable", fileCacheControl = "public, max-age=3600", routeOptions } = options;
|
|
135
|
+
let serverBuild = devServer == null ? build ?? createBuildLoader(devServer, path.resolve(serverBuildPath)) : createBuildLoader(devServer, path.resolve(serverBuildPath));
|
|
136
|
+
if (devServer == null) await registerStaticFiles(fastify, {
|
|
137
|
+
basePath,
|
|
138
|
+
clientBuildDirectory: path.resolve(clientBuildDirectory),
|
|
139
|
+
assetCacheControl,
|
|
140
|
+
fileCacheControl,
|
|
141
|
+
staticOptions
|
|
142
|
+
});
|
|
143
|
+
let handler = createRequestHandler({
|
|
144
|
+
build: serverBuild,
|
|
145
|
+
getLoadContext,
|
|
146
|
+
mode
|
|
147
|
+
});
|
|
148
|
+
fastify.removeAllContentTypeParsers();
|
|
149
|
+
fastify.addContentTypeParser("*", (_request, payload, done) => {
|
|
150
|
+
done(null, payload);
|
|
151
|
+
});
|
|
152
|
+
if (routeOptions) fastify.all("*", routeOptions, handler);
|
|
153
|
+
else fastify.all("*", handler);
|
|
154
|
+
}
|
|
155
|
+
function createBuildLoader(devServer, serverBuildPath) {
|
|
156
|
+
if (devServer != null) return () => importSsrModule(devServer, "virtual:react-router/server-build");
|
|
157
|
+
return async () => import(
|
|
158
|
+
/* @vite-ignore */
|
|
159
|
+
pathToFileURL(serverBuildPath).href
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
async function registerStaticFiles(fastify, options) {
|
|
163
|
+
let assetsDirectory = path.join(options.clientBuildDirectory, "assets");
|
|
164
|
+
await fastify.register(fastifyStatic, {
|
|
165
|
+
root: options.clientBuildDirectory,
|
|
166
|
+
prefix: options.basePath,
|
|
167
|
+
wildcard: false,
|
|
168
|
+
cacheControl: false,
|
|
169
|
+
dotfiles: "ignore",
|
|
170
|
+
etag: true,
|
|
171
|
+
lastModified: true,
|
|
172
|
+
setHeaders(res, filePath) {
|
|
173
|
+
let isAsset = filePath.startsWith(assetsDirectory);
|
|
174
|
+
res.setHeader("cache-control", isAsset ? options.assetCacheControl : options.fileCacheControl);
|
|
175
|
+
},
|
|
176
|
+
...options.staticOptions
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
//#endregion
|
|
180
|
+
export { createHeaders, createRequest, createRequestHandler, createUrl, fastifyReactRouter, sendResponse };
|
|
181
|
+
|
|
182
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["withCookies","createReactRouterHandler"],"sources":["../src/request.ts","../src/response.ts","../src/handler.ts","../src/fastify.ts"],"sourcesContent":["import { Readable } from \"node:stream\"\n\nimport { createReadableStreamFromReadable } from \"@react-router/node\"\nimport type { FastifyReply, FastifyRequest } from \"fastify\"\n\n/**\n * Copies Fastify's normalized request headers into Web Fetch `Headers`.\n *\n * @param source Fastify request headers.\n * @returns Fetch-compatible headers.\n */\nexport function createHeaders(source: FastifyRequest[\"headers\"]): Headers {\n let headers = new Headers()\n\n for (let [name, value] of Object.entries(source)) {\n if (value == null) continue\n\n if (Array.isArray(value)) {\n for (let item of value) {\n headers.append(name, item)\n }\n } else {\n headers.set(name, value)\n }\n }\n\n return headers\n}\n\n/**\n * Builds the absolute request URL that React Router expects.\n *\n * @param request Fastify request.\n * @returns Absolute URL for the original incoming request.\n */\nexport function createUrl(request: FastifyRequest): string {\n return `${request.protocol}://${request.host}${request.originalUrl}`\n}\n\n/**\n * Adapts a Fastify request to a Web Fetch `Request`.\n *\n * @param request Fastify request.\n * @param reply Fastify reply, used to abort work when the connection closes.\n * @returns Fetch-compatible request for React Router.\n */\nexport function createRequest(\n request: FastifyRequest,\n reply: FastifyReply,\n): Request {\n let controller: AbortController | null = new AbortController()\n\n let init: RequestInit & { duplex?: \"half\" } = {\n method: request.method,\n headers: createHeaders(request.headers),\n signal: controller.signal,\n }\n\n reply.raw.once(\"finish\", () => {\n controller = null\n })\n reply.raw.once(\"close\", () => {\n controller?.abort()\n })\n\n if ([\"GET\", \"HEAD\"].includes(request.method) === false) {\n init.body = getBody(request)\n init.duplex = \"half\"\n }\n\n return new Request(createUrl(request), init)\n}\n\nfunction getBody(request: FastifyRequest): BodyInit | null {\n let body = request.body\n\n if (body == null) return createReadableStreamFromReadable(request.raw)\n if (body instanceof Readable) return createReadableStreamFromReadable(body)\n if (body instanceof ReadableStream) return body\n if (body instanceof URLSearchParams) return body\n if (body instanceof ArrayBuffer) return body\n if (body instanceof Blob) return body\n if (body instanceof FormData) return body\n if (ArrayBuffer.isView(body)) {\n return new Uint8Array(\n body.buffer as ArrayBuffer,\n body.byteOffset,\n body.byteLength,\n )\n }\n if (typeof body === \"string\") return body\n\n return JSON.stringify(body)\n}\n","import { Readable } from \"node:stream\"\n\nimport type { FastifyReply } from \"fastify\"\n\n/**\n * Writes a Web Fetch `Response` through a Fastify reply.\n *\n * @param reply Fastify reply.\n * @param response React Router response.\n * @returns A promise that settles after the response is sent.\n */\nexport async function sendResponse(\n reply: FastifyReply,\n response: Response,\n): Promise<void> {\n reply.status(response.status)\n writeHeaders(reply, response.headers)\n\n if (response.body == null) {\n return reply.send()\n }\n\n return reply.send(readableFromWeb(response.body))\n}\n\nfunction writeHeaders(reply: FastifyReply, headers: Headers): void {\n let cookies = readSetCookies(headers)\n\n for (let [name, value] of headers) {\n if (name.toLowerCase() === \"set-cookie\" && cookies.length > 0) continue\n reply.header(name, value)\n }\n\n if (cookies.length > 0) {\n reply.header(\"set-cookie\", cookies)\n }\n}\n\nfunction readSetCookies(headers: Headers): string[] {\n let withCookies = headers as Headers & { getSetCookie?: () => string[] }\n let cookies = withCookies.getSetCookie?.()\n if (cookies && cookies.length > 0) return cookies\n\n let cookie = headers.get(\"Set-Cookie\")\n return cookie ? [cookie] : []\n}\n\nfunction readableFromWeb(body: ReadableStream<Uint8Array>): Readable {\n let reader = body.getReader()\n\n return new Readable({\n read() {\n reader.read().then(\n ({ done, value }) => {\n this.push(done ? null : Buffer.from(value))\n },\n (error: unknown) => {\n this.destroy(\n error instanceof Error ? error : new Error(String(error)),\n )\n },\n )\n },\n })\n}\n","import type { FastifyReply, FastifyRequest } from \"fastify\"\nimport type { RouterContextProvider, ServerBuild } from \"react-router\"\nimport { createRequestHandler as createReactRouterHandler } from \"react-router\"\n\nimport { createRequest } from \"./request.ts\"\nimport { sendResponse } from \"./response.ts\"\n\nexport type HttpRequest = FastifyRequest[\"raw\"]\nexport type HttpResponse = FastifyReply[\"raw\"]\n\nexport type ReactRouterLoadContext = RouterContextProvider\n\nexport type GetLoadContextFunction = (\n request: FastifyRequest,\n reply: FastifyReply,\n) =>\n | ReactRouterLoadContext\n | undefined\n | Promise<ReactRouterLoadContext | undefined>\n\nexport type RequestHandler = (\n request: FastifyRequest,\n reply: FastifyReply,\n) => Promise<void>\n\nexport interface CreateRequestHandlerOptions {\n build: ServerBuild | (() => ServerBuild | Promise<ServerBuild>)\n getLoadContext?: GetLoadContextFunction\n mode?: string\n}\n\n/**\n * Creates a Fastify route handler backed by React Router's server runtime.\n *\n * @param options React Router build, mode, and optional load context hook.\n * @returns Fastify route handler.\n */\nexport function createRequestHandler(\n options: CreateRequestHandlerOptions,\n): RequestHandler {\n let reactRouterHandler = createReactRouterHandler(\n options.build,\n options.mode ?? process.env.NODE_ENV,\n )\n\n return async (request, reply) => {\n let webRequest = createRequest(request, reply)\n let context = await options.getLoadContext?.(request, reply)\n let webResponse = await reactRouterHandler(webRequest, context)\n await sendResponse(reply, webResponse)\n }\n}\n","import path from \"node:path\"\nimport { pathToFileURL } from \"node:url\"\n\nimport fastifyStatic from \"@fastify/static\"\nimport type { FastifyStaticOptions } from \"@fastify/static\"\nimport type { FastifyInstance, RouteShorthandOptions } from \"fastify\"\nimport type { ServerBuild } from \"react-router\"\nimport type { ViteDevServer } from \"vite\"\n\nimport { createRequestHandler, type GetLoadContextFunction } from \"./handler.ts\"\nimport { importSsrModule } from \"./vite-runtime.ts\"\n\nexport interface FastifyReactRouterOptions {\n devServer?: ViteDevServer\n basePath?: string\n serverBuildPath?: string\n clientBuildDirectory?: string\n mode?: string\n getLoadContext?: GetLoadContextFunction\n build?: ServerBuild | (() => ServerBuild | Promise<ServerBuild>)\n staticOptions?: FastifyStaticOptions\n assetCacheControl?: string\n fileCacheControl?: string\n routeOptions?: RouteShorthandOptions\n}\n\n/**\n * Fastify plugin that serves React Router framework builds.\n *\n * @param fastify Fastify instance.\n * @param options Adapter options.\n */\nexport async function fastifyReactRouter(\n fastify: FastifyInstance,\n options: FastifyReactRouterOptions,\n): Promise<void> {\n let {\n devServer,\n basePath = \"/\",\n serverBuildPath = \"build/server/index.js\",\n clientBuildDirectory = \"build/client\",\n mode = process.env.NODE_ENV,\n getLoadContext,\n build,\n staticOptions,\n assetCacheControl = \"public, max-age=31536000, immutable\",\n fileCacheControl = \"public, max-age=3600\",\n routeOptions,\n } = options\n\n let serverBuild =\n devServer == null\n ? build ?? createBuildLoader(devServer, path.resolve(serverBuildPath))\n : createBuildLoader(devServer, path.resolve(serverBuildPath))\n\n if (devServer == null) {\n await registerStaticFiles(fastify, {\n basePath,\n clientBuildDirectory: path.resolve(clientBuildDirectory),\n assetCacheControl,\n fileCacheControl,\n staticOptions,\n })\n }\n\n let handler = createRequestHandler({\n build: serverBuild,\n getLoadContext,\n mode,\n })\n\n fastify.removeAllContentTypeParsers()\n fastify.addContentTypeParser(\"*\", (_request, payload, done) => {\n done(null, payload)\n })\n\n if (routeOptions) {\n fastify.all(\"*\", routeOptions, handler)\n } else {\n fastify.all(\"*\", handler)\n }\n}\n\nfunction createBuildLoader(\n devServer: ViteDevServer | undefined,\n serverBuildPath: string,\n): ServerBuild | (() => ServerBuild | Promise<ServerBuild>) {\n if (devServer != null) {\n return () =>\n importSsrModule<ServerBuild>(\n devServer,\n \"virtual:react-router/server-build\",\n )\n }\n\n return async () =>\n import(\n /* @vite-ignore */ pathToFileURL(serverBuildPath).href\n ) as Promise<ServerBuild>\n}\n\nasync function registerStaticFiles(\n fastify: FastifyInstance,\n options: {\n basePath: string\n clientBuildDirectory: string\n assetCacheControl: string\n fileCacheControl: string\n staticOptions?: FastifyStaticOptions\n },\n): Promise<void> {\n let assetsDirectory = path.join(options.clientBuildDirectory, \"assets\")\n\n await fastify.register(fastifyStatic, {\n root: options.clientBuildDirectory,\n prefix: options.basePath,\n wildcard: false,\n cacheControl: false,\n dotfiles: \"ignore\",\n etag: true,\n lastModified: true,\n setHeaders(res, filePath) {\n let isAsset = filePath.startsWith(assetsDirectory)\n res.setHeader(\n \"cache-control\",\n isAsset ? options.assetCacheControl : options.fileCacheControl,\n )\n },\n ...options.staticOptions,\n })\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
//#region src/vite-runtime.ts
|
|
2
|
+
/**
|
|
3
|
+
* Imports an SSR module using Vite's current Environment runner, with
|
|
4
|
+
* `ssrLoadModule` as a compatibility fallback.
|
|
5
|
+
*
|
|
6
|
+
* @param vite Vite dev server.
|
|
7
|
+
* @param id Module ID to import.
|
|
8
|
+
* @returns Imported module namespace.
|
|
9
|
+
*/
|
|
10
|
+
async function importSsrModule(vite, id) {
|
|
11
|
+
let ssr = vite.environments?.ssr;
|
|
12
|
+
if (typeof ssr?.runner?.import === "function") return ssr.runner.import(id);
|
|
13
|
+
return vite.ssrLoadModule(id);
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
16
|
+
export { importSsrModule as t };
|
|
17
|
+
|
|
18
|
+
//# sourceMappingURL=vite-runtime-Cuj_Fjkd.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vite-runtime-Cuj_Fjkd.mjs","names":[],"sources":["../src/vite-runtime.ts"],"sourcesContent":["import type { DevEnvironment, ViteDevServer } from \"vite\"\n\ninterface RunnableEnvironment extends DevEnvironment {\n runner: {\n import(id: string): Promise<Record<string, unknown>>\n }\n}\n\n/**\n * Imports an SSR module using Vite's current Environment runner, with\n * `ssrLoadModule` as a compatibility fallback.\n *\n * @param vite Vite dev server.\n * @param id Module ID to import.\n * @returns Imported module namespace.\n */\nexport async function importSsrModule<T = Record<string, unknown>>(\n vite: ViteDevServer,\n id: string,\n): Promise<T> {\n let ssr = vite.environments?.ssr as Partial<RunnableEnvironment> | undefined\n if (typeof ssr?.runner?.import === \"function\") {\n return ssr.runner.import(id) as Promise<T>\n }\n\n return vite.ssrLoadModule(id) as unknown as Promise<T>\n}\n"],"mappings":";;;;;;;;;AAgBA,eAAsB,gBACpB,MACA,IACY;CACZ,IAAI,MAAM,KAAK,cAAc;CAC7B,IAAI,OAAO,KAAK,QAAQ,WAAW,YACjC,OAAO,IAAI,OAAO,OAAO,EAAE;CAG7B,OAAO,KAAK,cAAc,EAAE;AAC9B"}
|
package/dist/vite.d.mts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { FastifyInstance } from "fastify";
|
|
2
|
+
import { Plugin, ViteDevServer } from "vite";
|
|
3
|
+
|
|
4
|
+
//#region src/vite.d.ts
|
|
5
|
+
type FastifyAppFactory = (vite: ViteDevServer) => FastifyInstance | Promise<FastifyInstance>;
|
|
6
|
+
interface FastifyReactRouterDevOptions {
|
|
7
|
+
/** Server module loaded by Vite during `react-router dev`. */
|
|
8
|
+
entry?: string;
|
|
9
|
+
/** Named export that creates the Fastify app. Falls back to `default`. */
|
|
10
|
+
exportName?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Keeps local modules imported by the Fastify server entry external in the
|
|
13
|
+
* React Router SSR build. This preserves singleton module identity for shared
|
|
14
|
+
* values such as React Router context tokens.
|
|
15
|
+
*
|
|
16
|
+
* Pass an array to externalize explicit import specifiers instead.
|
|
17
|
+
*/
|
|
18
|
+
externalizeServerEntryImports?: boolean | string[];
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Vite plugin that lets `react-router dev` serve through a Fastify app.
|
|
22
|
+
*
|
|
23
|
+
* Vite continues to handle its internal client/HMR/module middleware first.
|
|
24
|
+
* Fastify receives the remaining requests, including the React Router catch-all
|
|
25
|
+
* installed by `fastifyReactRouter`.
|
|
26
|
+
*
|
|
27
|
+
* @param options Development server entry options.
|
|
28
|
+
* @returns Vite plugin.
|
|
29
|
+
*/
|
|
30
|
+
declare function fastifyReactRouterDev(options?: FastifyReactRouterDevOptions): Plugin;
|
|
31
|
+
//#endregion
|
|
32
|
+
export { FastifyAppFactory, FastifyReactRouterDevOptions, fastifyReactRouterDev };
|
|
33
|
+
//# sourceMappingURL=vite.d.mts.map
|