@nifrajs/web 2.2.0 → 2.4.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/README.md +3 -3
- package/dist/build-vite.d.ts +4 -4
- package/dist/build-vite.d.ts.map +1 -1
- package/dist/build-vite.js +20 -74
- package/dist/build-vite.js.map +1 -1
- package/dist/build.d.ts +64 -42
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +100 -78
- package/dist/build.js.map +1 -1
- package/dist/bun-dev-entry.d.ts +1 -1
- package/dist/bun-dev-entry.js +1 -1
- package/dist/chunk-names.js +1 -1
- package/dist/client.d.ts +3 -9
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +201 -29
- package/dist/client.js.map +1 -1
- package/dist/conformance.d.ts +2 -2
- package/dist/conformance.js +3 -3
- package/dist/deferred.d.ts +11 -11
- package/dist/deferred.d.ts.map +1 -1
- package/dist/deferred.js +16 -16
- package/dist/dev-diagnostics.d.ts +20 -0
- package/dist/dev-diagnostics.d.ts.map +1 -0
- package/dist/dev-diagnostics.js +34 -0
- package/dist/dev-diagnostics.js.map +1 -0
- package/dist/dev-error.d.ts +10 -13
- package/dist/dev-error.d.ts.map +1 -1
- package/dist/dev-error.js +51 -37
- package/dist/dev-error.js.map +1 -1
- package/dist/dev-port.d.ts +3 -3
- package/dist/dev-port.d.ts.map +1 -1
- package/dist/dev-port.js +10 -4
- package/dist/dev-port.js.map +1 -1
- package/dist/dev.d.ts +4 -4
- package/dist/dev.d.ts.map +1 -1
- package/dist/dev.js +28 -12
- package/dist/dev.js.map +1 -1
- package/dist/diagnostic.d.ts +93 -0
- package/dist/diagnostic.d.ts.map +1 -0
- package/dist/diagnostic.js +205 -0
- package/dist/diagnostic.js.map +1 -0
- package/dist/draft.d.ts +12 -12
- package/dist/draft.js +11 -11
- package/dist/fn-state.d.ts +56 -0
- package/dist/fn-state.d.ts.map +1 -0
- package/dist/fn-state.js +77 -0
- package/dist/fn-state.js.map +1 -0
- package/dist/fn.d.ts +111 -0
- package/dist/fn.d.ts.map +1 -0
- package/dist/fn.js +125 -0
- package/dist/fn.js.map +1 -0
- package/dist/fonts-google.d.ts +9 -9
- package/dist/fonts-google.js +10 -10
- package/dist/fonts.d.ts +7 -7
- package/dist/fonts.js +1 -1
- package/dist/forms.d.ts +8 -8
- package/dist/fs.d.ts +1 -1
- package/dist/fs.js +1 -1
- package/dist/index.d.ts +151 -70
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +317 -139
- package/dist/index.js.map +1 -1
- package/dist/internal/head-attributes.d.ts +15 -0
- package/dist/internal/head-attributes.d.ts.map +1 -0
- package/dist/internal/head-attributes.js +99 -0
- package/dist/internal/head-attributes.js.map +1 -0
- package/dist/internal/script-types.d.ts +14 -0
- package/dist/internal/script-types.d.ts.map +1 -0
- package/dist/internal/script-types.js +17 -0
- package/dist/internal/script-types.js.map +1 -0
- package/dist/internal/server-boundary.d.ts +60 -0
- package/dist/internal/server-boundary.d.ts.map +1 -0
- package/dist/internal/server-boundary.js +174 -0
- package/dist/internal/server-boundary.js.map +1 -0
- package/dist/internal/timing-safe-equal.d.ts +1 -1
- package/dist/internal/timing-safe-equal.js +1 -1
- package/dist/internal/vite-import.d.ts +63 -0
- package/dist/internal/vite-import.d.ts.map +1 -0
- package/dist/internal/vite-import.js +94 -0
- package/dist/internal/vite-import.js.map +1 -0
- package/dist/islands.d.ts +5 -5
- package/dist/islands.js +5 -5
- package/dist/isr.d.ts +15 -15
- package/dist/isr.js +7 -7
- package/dist/manifest.d.ts +73 -35
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +10 -16
- package/dist/manifest.js.map +1 -1
- package/dist/module-graph.d.ts +5 -5
- package/dist/module-graph.js +4 -4
- package/dist/navigation.d.ts +120 -5
- package/dist/navigation.d.ts.map +1 -1
- package/dist/navigation.js +37 -5
- package/dist/navigation.js.map +1 -1
- package/dist/plugins/css-modules.d.ts +3 -3
- package/dist/plugins/css-modules.js +0 -0
- package/dist/plugins/kit.d.ts +8 -8
- package/dist/plugins/kit.js +9 -9
- package/dist/plugins/scss.js +3 -3
- package/dist/plugins/vite-leak-guard.d.ts +1 -1
- package/dist/plugins/vite-leak-guard.js +1 -1
- package/dist/plugins/vite-server-fn.d.ts +23 -0
- package/dist/plugins/vite-server-fn.d.ts.map +1 -0
- package/dist/plugins/vite-server-fn.js +51 -0
- package/dist/plugins/vite-server-fn.js.map +1 -0
- package/dist/plugins/vite-server-only.d.ts +49 -0
- package/dist/plugins/vite-server-only.d.ts.map +1 -0
- package/dist/plugins/vite-server-only.js +57 -0
- package/dist/plugins/vite-server-only.js.map +1 -0
- package/dist/prerender.d.ts +5 -5
- package/dist/prerender.js +7 -7
- package/dist/query.d.ts +10 -10
- package/dist/query.js +15 -15
- package/dist/route-manifest.d.ts +1 -1
- package/dist/route-manifest.js +2 -2
- package/dist/router.d.ts +30 -21
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +62 -32
- package/dist/router.js.map +1 -1
- package/dist/search.d.ts +94 -0
- package/dist/search.d.ts.map +1 -0
- package/dist/search.js +232 -0
- package/dist/search.js.map +1 -0
- package/dist/server-only.d.ts +6 -6
- package/dist/server-only.js +7 -7
- package/dist/service-worker.d.ts +56 -0
- package/dist/service-worker.d.ts.map +1 -0
- package/dist/service-worker.js +127 -0
- package/dist/service-worker.js.map +1 -0
- package/dist/vite.d.ts +16 -14
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +51 -24
- package/dist/vite.js.map +1 -1
- package/package.json +38 -4
- package/server-manifest.d.ts +31 -0
- package/src/build-vite.ts +24 -77
- package/src/build.ts +137 -105
- package/src/bun-dev-entry.ts +1 -1
- package/src/chunk-names.ts +1 -1
- package/src/client.ts +215 -32
- package/src/conformance.ts +3 -3
- package/src/deferred.ts +17 -17
- package/src/dev-diagnostics.ts +47 -0
- package/src/dev-error.ts +64 -33
- package/src/dev-port.ts +17 -5
- package/src/dev.ts +36 -20
- package/src/diagnostic.ts +284 -0
- package/src/draft.ts +15 -15
- package/src/fn-state.ts +107 -0
- package/src/fn.ts +199 -0
- package/src/fonts-google.ts +13 -13
- package/src/fonts.ts +7 -7
- package/src/forms.ts +8 -8
- package/src/fs.ts +2 -2
- package/src/index.ts +460 -181
- package/src/internal/head-attributes.ts +109 -0
- package/src/internal/script-types.ts +18 -0
- package/src/internal/server-boundary.ts +187 -0
- package/src/internal/timing-safe-equal.ts +1 -1
- package/src/internal/vite-import.ts +92 -0
- package/src/islands.ts +7 -7
- package/src/isr.ts +17 -17
- package/src/manifest.ts +83 -42
- package/src/module-graph.ts +6 -6
- package/src/navigation.ts +151 -5
- package/src/plugins/css-modules.ts +0 -0
- package/src/plugins/kit.ts +11 -11
- package/src/plugins/scss.ts +3 -3
- package/src/plugins/vite-leak-guard.ts +2 -2
- package/src/plugins/vite-server-fn.ts +67 -0
- package/src/plugins/vite-server-only.ts +75 -0
- package/src/prerender.ts +10 -10
- package/src/query.ts +22 -22
- package/src/route-manifest.ts +3 -3
- package/src/router.ts +98 -45
- package/src/search.ts +297 -0
- package/src/server-only.ts +7 -7
- package/src/service-worker.ts +165 -0
- package/src/vite.ts +61 -32
package/src/vite.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `@nifrajs/web/vite`
|
|
2
|
+
* `@nifrajs/web/vite` - a **dev-only** server with true HMR, backed by Vite in middleware mode. nifra's
|
|
3
3
|
* production pipeline stays Bun-native (`@nifrajs/web/build` → `Bun.build`); this is purely for the dev
|
|
4
4
|
* loop. Bun's own HMR is dev-server-only and DCE's `import.meta.hot` under `Bun.build` (nifra's bundler),
|
|
5
5
|
* so HMR comes from Vite + the framework's official Vite plugin (React Fast Refresh, Vue/Svelte/Solid
|
|
6
|
-
* HMR)
|
|
6
|
+
* HMR) - which you inject via `plugins` (the same structural-injection idiom as the Bun/codec plugins).
|
|
7
7
|
*
|
|
8
8
|
* Flow: Vite serves + HMR-swaps the client modules (nifra's codegen'd entry + the route files); nifra
|
|
9
9
|
* still **SSRs** each request, and the rendered HTML is run through `vite.transformIndexHtml` so Vite
|
|
10
10
|
* injects its HMR client + the framework's refresh preamble. Node `http` (not `Bun.serve`) because
|
|
11
|
-
* Vite's `middlewares` are Connect-style
|
|
11
|
+
* Vite's `middlewares` are Connect-style - it runs fine under Bun.
|
|
12
12
|
*/
|
|
13
13
|
import { writeFileSync } from "node:fs"
|
|
14
14
|
import {
|
|
@@ -17,12 +17,16 @@ import {
|
|
|
17
17
|
type ServerResponse,
|
|
18
18
|
} from "node:http"
|
|
19
19
|
import { relative, resolve as resolvePath } from "node:path"
|
|
20
|
-
import {
|
|
20
|
+
import { createDevDiagnostics } from "./dev-diagnostics.ts"
|
|
21
21
|
import { listenOrExplain } from "./dev-port.ts"
|
|
22
22
|
import { discoverRoutes } from "./fs.ts"
|
|
23
23
|
import { DEFAULT_DEV_PORT, generateClientEntry } from "./index.ts"
|
|
24
|
+
import { vitePublicEnvPrefix } from "./internal/server-boundary.ts"
|
|
25
|
+
import { importVite } from "./internal/vite-import.ts"
|
|
26
|
+
import { viteServerFnStub } from "./plugins/vite-server-fn.ts"
|
|
27
|
+
import { viteServerOnlyEmpty } from "./plugins/vite-server-only.ts"
|
|
24
28
|
|
|
25
|
-
/** Minimal app surface
|
|
29
|
+
/** Minimal app surface - `createWebApp(...)` satisfies it. */
|
|
26
30
|
interface FetchApp {
|
|
27
31
|
fetch(request: Request): Response | Promise<Response>
|
|
28
32
|
}
|
|
@@ -45,7 +49,7 @@ interface ViteModule {
|
|
|
45
49
|
readonly rolldownVersion?: string
|
|
46
50
|
}
|
|
47
51
|
|
|
48
|
-
// `node:http` server type the request handler runs on (also the HMR WebSocket host
|
|
52
|
+
// `node:http` server type the request handler runs on (also the HMR WebSocket host - see below).
|
|
49
53
|
type NodeHttpServer = ReturnType<typeof createHttpServer>
|
|
50
54
|
|
|
51
55
|
export interface ViteDevServerOptions {
|
|
@@ -66,10 +70,10 @@ export interface ViteDevServerOptions {
|
|
|
66
70
|
clientEntry: string,
|
|
67
71
|
load: (absolutePath: string) => Promise<unknown>,
|
|
68
72
|
) => FetchApp | Promise<FetchApp>
|
|
69
|
-
/** Vite plugins
|
|
73
|
+
/** Vite plugins - inject your framework's official plugin, e.g. `[react()]`. */
|
|
70
74
|
readonly plugins?: readonly unknown[]
|
|
71
75
|
/**
|
|
72
|
-
* Extra `resolve.conditions` prepended ahead of nifra's defaults
|
|
76
|
+
* Extra `resolve.conditions` prepended ahead of nifra's defaults - some frameworks need their own
|
|
73
77
|
* (e.g. Solid's `"solid"` condition routes `solid-js` to its source/JSX-dev build).
|
|
74
78
|
*/
|
|
75
79
|
readonly conditions?: readonly string[]
|
|
@@ -84,12 +88,14 @@ export interface ViteDevServerOptions {
|
|
|
84
88
|
readonly port?: number
|
|
85
89
|
/**
|
|
86
90
|
* Use polling for the file watcher. Native fs events (fsevents/inotify) are unreliable inside
|
|
87
|
-
* containers, networked filesystems, and some sandboxes
|
|
91
|
+
* containers, networked filesystems, and some sandboxes - there, HMR silently never fires. Set
|
|
88
92
|
* `true` (or the env var `CHOKIDAR_USEPOLLING=1`) to poll instead. Default: off (native events).
|
|
89
93
|
*/
|
|
90
94
|
readonly poll?: boolean
|
|
91
95
|
/** Vite public directory. Defaults to `<root>/public`; `false` disables it. */
|
|
92
96
|
readonly publicDir?: string | false
|
|
97
|
+
/** Client-visible environment prefix (default `"PUBLIC_"`; empty disables exposure). */
|
|
98
|
+
readonly publicEnvPrefix?: string
|
|
93
99
|
}
|
|
94
100
|
|
|
95
101
|
export interface ViteDevServer {
|
|
@@ -97,6 +103,8 @@ export interface ViteDevServer {
|
|
|
97
103
|
stop(): Promise<void>
|
|
98
104
|
}
|
|
99
105
|
|
|
106
|
+
export { LAST_ERROR_PATH } from "./diagnostic.ts"
|
|
107
|
+
|
|
100
108
|
// The codegen'd client entry is written here (at the Vite root) so Vite serves + HMRs it.
|
|
101
109
|
const DEV_ENTRY = ".nifra-vite-entry.tsx"
|
|
102
110
|
|
|
@@ -128,11 +136,11 @@ function toWebRequest(req: IncomingMessage, body: Buffer | undefined): Request {
|
|
|
128
136
|
}
|
|
129
137
|
|
|
130
138
|
// A Vite `config` hook: a plain function, OR the object form `{ handler, order }` Vite accepts for hook
|
|
131
|
-
// ordering (`order: "pre" | "post"`). We wrap the handler in either shape
|
|
139
|
+
// ordering (`order: "pre" | "post"`). We wrap the handler in either shape - see normalizeRolldownPlugins.
|
|
132
140
|
type ConfigFn = (config: unknown, env: unknown) => unknown
|
|
133
141
|
type ConfigHook = ConfigFn | { readonly handler: ConfigFn; readonly order?: unknown }
|
|
134
142
|
|
|
135
|
-
// A Vite plugin
|
|
143
|
+
// A Vite plugin - the only hook we wrap is `config`. Typed structurally (no `vite` type dep). Everything
|
|
136
144
|
// else on the plugin object is preserved by spread, so wrapping is transparent to Vite.
|
|
137
145
|
interface VitePluginLike {
|
|
138
146
|
readonly name?: string
|
|
@@ -140,7 +148,7 @@ interface VitePluginLike {
|
|
|
140
148
|
readonly [key: string]: unknown
|
|
141
149
|
}
|
|
142
150
|
|
|
143
|
-
/** The bits of a Node ServerResponse `pipeWebBodyToNode` touches
|
|
151
|
+
/** The bits of a Node ServerResponse `pipeWebBodyToNode` touches - structural, to avoid a node:http dep here. */
|
|
144
152
|
interface NodeResLike {
|
|
145
153
|
flushHeaders?(): void
|
|
146
154
|
on(event: "close", cb: () => void): void
|
|
@@ -155,7 +163,7 @@ interface NodeHeaderSink {
|
|
|
155
163
|
|
|
156
164
|
/**
|
|
157
165
|
* Copy a Web `Response`'s headers onto a Node response, emitting EACH `Set-Cookie` as its own header. The
|
|
158
|
-
* `Headers` iterator (and `.get`) join multiple set-cookie values with ", ", which corrupts cookies
|
|
166
|
+
* `Headers` iterator (and `.get`) join multiple set-cookie values with ", ", which corrupts cookies - e.g.
|
|
159
167
|
* better-auth's `session_token` + `session_data` collapse into one unparseable cookie and the session is
|
|
160
168
|
* silently lost. `getSetCookie()` returns them split; Node's `setHeader` emits one header per array element.
|
|
161
169
|
*/
|
|
@@ -170,7 +178,7 @@ export function applyResponseHeaders(headers: Headers, res: NodeHeaderSink): voi
|
|
|
170
178
|
|
|
171
179
|
/**
|
|
172
180
|
* Stream a Web `Response` body to a Node response chunk-by-chunk. Buffering the whole body (e.g.
|
|
173
|
-
* `arrayBuffer()`) waits for the stream to END
|
|
181
|
+
* `arrayBuffer()`) waits for the stream to END - which an open-ended SSE (`text/event-stream`) body never
|
|
174
182
|
* does, so it hung `nifra dev` (the Bun production server streamed it fine). This flushes each chunk as it
|
|
175
183
|
* arrives and cancels the reader if the client disconnects; a finite body just streams its chunk(s) + ends.
|
|
176
184
|
*/
|
|
@@ -192,23 +200,23 @@ export async function pipeWebBodyToNode(
|
|
|
192
200
|
res.write(value)
|
|
193
201
|
}
|
|
194
202
|
} catch {
|
|
195
|
-
// client disconnected mid-stream
|
|
203
|
+
// client disconnected mid-stream - the `close` handler already cancelled the reader
|
|
196
204
|
}
|
|
197
205
|
res.end()
|
|
198
206
|
}
|
|
199
207
|
|
|
200
208
|
/**
|
|
201
209
|
* Strip `optimizeDeps.rollupOptions.jsx` from a plugin's `config` hook output when running under
|
|
202
|
-
* rolldown-vite
|
|
210
|
+
* rolldown-vite - the source of the scary, harmless `Warning: Invalid input options … "jsx" Invalid
|
|
203
211
|
* key: Expected never but received "jsx"` on `nifra dev`.
|
|
204
212
|
*
|
|
205
213
|
* Why it happens: `@vitejs/plugin-react@4.x` (and peers) target an *older* rolldown-vite optimizeDeps
|
|
206
|
-
* API
|
|
214
|
+
* API - they inject `optimizeDeps.rollupOptions.jsx` to tell the dep pre-bundler to transform JSX. Vite
|
|
207
215
|
* 8's rolldown dep-optimizer renamed that surface to `optimizeDeps.rolldownOptions` (and moved jsx under
|
|
208
216
|
* `transform.jsx`), so the stale `rollupOptions.jsx` is an unrecognized input option → the warning. It's
|
|
209
217
|
* a version-skew artifact, not a real misconfig: the route source JSX transform runs through the
|
|
210
218
|
* plugin's own `transform` hook (untouched here), and node_modules deps that get pre-bundled almost
|
|
211
|
-
* never contain raw JSX
|
|
219
|
+
* never contain raw JSX - so dropping the dead key changes no behavior and keeps HMR/Fast Refresh
|
|
212
220
|
* intact. We *strip* (rather than translate to `rolldownOptions`) so the fix is version-agnostic: a
|
|
213
221
|
* plugin already emitting the correct `rolldownOptions` is left untouched, and a future plugin bump that
|
|
214
222
|
* stops emitting `rollupOptions.jsx` makes this a no-op.
|
|
@@ -216,12 +224,12 @@ export async function pipeWebBodyToNode(
|
|
|
216
224
|
* Scoped narrowly: only the `optimizeDeps.rollupOptions.jsx` key is removed, only under rolldown-vite,
|
|
217
225
|
* and only from the value a plugin's `config` hook returns. Non-rolldown Vite is passed through verbatim.
|
|
218
226
|
*
|
|
219
|
-
* FLATTEN FIRST: a Vite plugin factory may return an ARRAY of plugins
|
|
227
|
+
* FLATTEN FIRST: a Vite plugin factory may return an ARRAY of plugins - `@vitejs/plugin-react`'s `react()`
|
|
220
228
|
* returns `[vite:react-babel, vite:react-refresh]`, and it's `react:react-babel`'s `config` hook that emits
|
|
221
229
|
* the offending `optimizeDeps.rollupOptions.jsx`. `nifra.config.ts` writes `vitePlugins = [react()]`, so the
|
|
222
230
|
* plugin list arrives NESTED (`[[babel, refresh]]`). Without flattening, `.map` sees the inner array (which
|
|
223
|
-
* has no `config`), leaves it untouched, and Vite
|
|
224
|
-
*
|
|
231
|
+
* has no `config`), leaves it untouched, and Vite - which flattens plugin arrays itself before running them
|
|
232
|
+
* - then executes the un-stripped babel hook, so the warning survives. Flattening here (Vite accepts a flat
|
|
225
233
|
* list identically) is what lets the strip reach every real plugin.
|
|
226
234
|
*/
|
|
227
235
|
export function normalizeRolldownPlugins(
|
|
@@ -251,7 +259,7 @@ export function normalizeRolldownPlugins(
|
|
|
251
259
|
if (typeof handler !== "function") return plugin
|
|
252
260
|
const wrappedHandler: ConfigFn = (config, env) => {
|
|
253
261
|
const returned = handler(config, env)
|
|
254
|
-
// The hook may return a promise
|
|
262
|
+
// The hook may return a promise - normalize both shapes.
|
|
255
263
|
return returned instanceof Promise ? returned.then(stripJsxKey) : stripJsxKey(returned)
|
|
256
264
|
}
|
|
257
265
|
// Preserve the ORIGINAL shape: a function stays a function; the object form keeps its `order`
|
|
@@ -285,14 +293,25 @@ export async function createViteDevServer(options: ViteDevServerOptions): Promis
|
|
|
285
293
|
const entryUrl = `/${DEV_ENTRY}`
|
|
286
294
|
|
|
287
295
|
// Create our HTTP server FIRST, then hand it to Vite as the HMR WebSocket host (`hmr.server`). In
|
|
288
|
-
// middleware mode Vite would otherwise open a *separate* ws port (24678)
|
|
296
|
+
// middleware mode Vite would otherwise open a *separate* ws port (24678) - fragile: it conflicts
|
|
289
297
|
// across restarts ("Port undefined is already in use" → the client never connects). Sharing one
|
|
290
298
|
// port means HMR rides the same origin as the app, robust across restarts. The handler closes over
|
|
291
299
|
// `vite`, which is assigned just below before the server starts listening.
|
|
292
300
|
let vite!: ViteLike
|
|
293
301
|
let app: FetchApp
|
|
302
|
+
// The most recent SSR failure, served as JSON at LAST_ERROR_PATH. Shared with the Bun adapter so the
|
|
303
|
+
// endpoint and its headers can't drift between the two dev servers.
|
|
304
|
+
const devDiagnostics = createDevDiagnostics(root)
|
|
294
305
|
|
|
295
306
|
const server: NodeHttpServer = createHttpServer((req, res) => {
|
|
307
|
+
// Keep this before Vite's middleware: it is an agent endpoint owned by nifra, not a file or app route.
|
|
308
|
+
if (devDiagnostics.isLastErrorPath((req.url ?? "/").split("?", 1)[0] ?? "/")) {
|
|
309
|
+
const { body, headers } = devDiagnostics.lastError()
|
|
310
|
+
res.statusCode = 200
|
|
311
|
+
for (const [key, value] of Object.entries(headers)) res.setHeader(key, value)
|
|
312
|
+
res.end(body)
|
|
313
|
+
return
|
|
314
|
+
}
|
|
296
315
|
vite.middlewares(req, res, () => {
|
|
297
316
|
// Not a Vite asset → nifra SSR. (`next` runs after Vite declines, so the body is still readable.)
|
|
298
317
|
void (async () => {
|
|
@@ -302,7 +321,7 @@ export async function createViteDevServer(options: ViteDevServerOptions): Promis
|
|
|
302
321
|
const contentType = nifraRes.headers.get("content-type") ?? ""
|
|
303
322
|
res.statusCode = nifraRes.status
|
|
304
323
|
if (!contentType.includes("text/html")) {
|
|
305
|
-
// Data / redirect / asset response
|
|
324
|
+
// Data / redirect / asset response - pass through untouched, streamed (SSE-safe). Set-Cookie is
|
|
306
325
|
// emitted per-header so multi-cookie responses (e.g. better-auth sessions) aren't collapsed.
|
|
307
326
|
applyResponseHeaders(nifraRes.headers, res)
|
|
308
327
|
await pipeWebBodyToNode(nifraRes.body, res)
|
|
@@ -314,29 +333,37 @@ export async function createViteDevServer(options: ViteDevServerOptions): Promis
|
|
|
314
333
|
res.end(html)
|
|
315
334
|
} catch (err) {
|
|
316
335
|
// Source-map the stack first (Vite maps the bundled frames back to your `.ts`), then render
|
|
317
|
-
// the readable dev overlay instead of a bare text dump. Dev-only
|
|
336
|
+
// the readable dev overlay instead of a bare text dump. Dev-only - production maps to `_error`.
|
|
318
337
|
if (err instanceof Error) vite.ssrFixStacktrace(err)
|
|
338
|
+
const html = devDiagnostics.capture(err, {
|
|
339
|
+
method: req.method ?? "GET",
|
|
340
|
+
url: req.url ?? "/",
|
|
341
|
+
})
|
|
319
342
|
res.statusCode = 500
|
|
320
343
|
res.setHeader("content-type", "text/html; charset=utf-8")
|
|
321
|
-
res.end(
|
|
344
|
+
res.end(html)
|
|
322
345
|
}
|
|
323
346
|
})()
|
|
324
347
|
})
|
|
325
348
|
})
|
|
326
349
|
|
|
327
350
|
// `conditions: ["bun"]` makes Vite resolve nifra's workspace packages (`@nifrajs/web-react/client`, …) to
|
|
328
|
-
// their TS **source**
|
|
351
|
+
// their TS **source** - so the dev server needs no prior `dist` build of the adapter packages.
|
|
329
352
|
const usePolling = options.poll ?? process.env.CHOKIDAR_USEPOLLING === "1"
|
|
330
|
-
|
|
353
|
+
// Never `import("vite")` directly here: the guard in `importVite` has to run first, and the dev
|
|
354
|
+
// server importing vite unguarded is precisely what poisoned the module for the whole process.
|
|
355
|
+
const viteModule = await importVite<ViteModule>()
|
|
331
356
|
const { createServer } = viteModule
|
|
332
357
|
// Under rolldown-vite (Vite 8+), strip the stale `optimizeDeps.rollupOptions.jsx` some framework
|
|
333
|
-
// plugins still emit
|
|
358
|
+
// plugins still emit - it triggers a noisy "Invalid key … jsx" warning but does nothing useful here.
|
|
334
359
|
const plugins = normalizeRolldownPlugins(
|
|
335
360
|
options.plugins ?? [],
|
|
336
361
|
viteModule.rolldownVersion !== undefined,
|
|
337
362
|
)
|
|
338
363
|
vite = await createServer({
|
|
339
364
|
root,
|
|
365
|
+
// Dev must not inherit Vite's default `VITE_*` exposure beside Nifra's documented boundary.
|
|
366
|
+
envPrefix: vitePublicEnvPrefix(options.publicEnvPrefix),
|
|
340
367
|
...(options.publicDir !== undefined ? { publicDir: options.publicDir } : {}),
|
|
341
368
|
appType: "custom",
|
|
342
369
|
server: {
|
|
@@ -345,13 +372,15 @@ export async function createViteDevServer(options: ViteDevServerOptions): Promis
|
|
|
345
372
|
// Explicit watch config; poll when native fs events aren't delivered (containers/sandboxes).
|
|
346
373
|
watch: usePolling ? { usePolling: true, interval: 80 } : {},
|
|
347
374
|
},
|
|
348
|
-
plugins:
|
|
375
|
+
// Ahead of the user's plugins: a `*.fn` module must be replaced before anything else
|
|
376
|
+
// reads it, and the dev server is a client bundler like any other.
|
|
377
|
+
plugins: [viteServerFnStub(), viteServerOnlyEmpty(), ...plugins],
|
|
349
378
|
resolve: {
|
|
350
379
|
conditions: [...(options.conditions ?? []), "bun", "module", "browser", "development"],
|
|
351
380
|
// Dedupe React to ONE copy. In a multi-root workspace a shared package can pull react/react-dom
|
|
352
381
|
// from a SIBLING app's node_modules, so the dev server loads two React cores → a second hook
|
|
353
382
|
// dispatcher → `resolveDispatcher().useState` null on any hook-using route (the error points at the
|
|
354
|
-
// component, not the resolution
|
|
383
|
+
// component, not the resolution - brutal to diagnose). Mirrors the build-time reactDedupePlugin so
|
|
355
384
|
// dev matches prod. No-op for non-React apps (the package simply isn't present to dedupe).
|
|
356
385
|
dedupe: ["react", "react-dom"],
|
|
357
386
|
},
|
|
@@ -383,7 +412,7 @@ export async function createViteDevServer(options: ViteDevServerOptions): Promis
|
|
|
383
412
|
}
|
|
384
413
|
|
|
385
414
|
try {
|
|
386
|
-
await listenOrExplain(server, port)
|
|
415
|
+
await listenOrExplain(server, port, "127.0.0.1")
|
|
387
416
|
} catch (err) {
|
|
388
417
|
// Nothing is listening, but Vite is fully up by now - watchers, the dep optimizer, its own sockets -
|
|
389
418
|
// and every one of those keeps the event loop alive. Left open, the process prints the diagnosis and
|