@nifrajs/web 2.2.0 → 2.3.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-error.d.ts +2 -2
- package/dist/dev-error.js +3 -3
- package/dist/dev-port.d.ts +2 -2
- package/dist/dev-port.js +1 -1
- package/dist/dev.d.ts +3 -3
- package/dist/dev.js +9 -9
- 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 +57 -28
- 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 +15 -13
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +31 -21
- package/dist/vite.js.map +1 -1
- package/package.json +33 -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-error.ts +3 -3
- package/src/dev-port.ts +2 -2
- package/src/dev.ts +12 -12
- 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 +91 -41
- package/src/search.ts +297 -0
- package/src/server-only.ts +7 -7
- package/src/service-worker.ts +165 -0
- package/src/vite.ts +41 -29
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 {
|
|
@@ -21,8 +21,12 @@ import { renderDevErrorOverlay } from "./dev-error.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 {
|
|
@@ -128,11 +134,11 @@ function toWebRequest(req: IncomingMessage, body: Buffer | undefined): Request {
|
|
|
128
134
|
}
|
|
129
135
|
|
|
130
136
|
// 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
|
|
137
|
+
// ordering (`order: "pre" | "post"`). We wrap the handler in either shape - see normalizeRolldownPlugins.
|
|
132
138
|
type ConfigFn = (config: unknown, env: unknown) => unknown
|
|
133
139
|
type ConfigHook = ConfigFn | { readonly handler: ConfigFn; readonly order?: unknown }
|
|
134
140
|
|
|
135
|
-
// A Vite plugin
|
|
141
|
+
// A Vite plugin - the only hook we wrap is `config`. Typed structurally (no `vite` type dep). Everything
|
|
136
142
|
// else on the plugin object is preserved by spread, so wrapping is transparent to Vite.
|
|
137
143
|
interface VitePluginLike {
|
|
138
144
|
readonly name?: string
|
|
@@ -140,7 +146,7 @@ interface VitePluginLike {
|
|
|
140
146
|
readonly [key: string]: unknown
|
|
141
147
|
}
|
|
142
148
|
|
|
143
|
-
/** The bits of a Node ServerResponse `pipeWebBodyToNode` touches
|
|
149
|
+
/** The bits of a Node ServerResponse `pipeWebBodyToNode` touches - structural, to avoid a node:http dep here. */
|
|
144
150
|
interface NodeResLike {
|
|
145
151
|
flushHeaders?(): void
|
|
146
152
|
on(event: "close", cb: () => void): void
|
|
@@ -155,7 +161,7 @@ interface NodeHeaderSink {
|
|
|
155
161
|
|
|
156
162
|
/**
|
|
157
163
|
* 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
|
|
164
|
+
* `Headers` iterator (and `.get`) join multiple set-cookie values with ", ", which corrupts cookies - e.g.
|
|
159
165
|
* better-auth's `session_token` + `session_data` collapse into one unparseable cookie and the session is
|
|
160
166
|
* silently lost. `getSetCookie()` returns them split; Node's `setHeader` emits one header per array element.
|
|
161
167
|
*/
|
|
@@ -170,7 +176,7 @@ export function applyResponseHeaders(headers: Headers, res: NodeHeaderSink): voi
|
|
|
170
176
|
|
|
171
177
|
/**
|
|
172
178
|
* 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
|
|
179
|
+
* `arrayBuffer()`) waits for the stream to END - which an open-ended SSE (`text/event-stream`) body never
|
|
174
180
|
* does, so it hung `nifra dev` (the Bun production server streamed it fine). This flushes each chunk as it
|
|
175
181
|
* arrives and cancels the reader if the client disconnects; a finite body just streams its chunk(s) + ends.
|
|
176
182
|
*/
|
|
@@ -192,23 +198,23 @@ export async function pipeWebBodyToNode(
|
|
|
192
198
|
res.write(value)
|
|
193
199
|
}
|
|
194
200
|
} catch {
|
|
195
|
-
// client disconnected mid-stream
|
|
201
|
+
// client disconnected mid-stream - the `close` handler already cancelled the reader
|
|
196
202
|
}
|
|
197
203
|
res.end()
|
|
198
204
|
}
|
|
199
205
|
|
|
200
206
|
/**
|
|
201
207
|
* Strip `optimizeDeps.rollupOptions.jsx` from a plugin's `config` hook output when running under
|
|
202
|
-
* rolldown-vite
|
|
208
|
+
* rolldown-vite - the source of the scary, harmless `Warning: Invalid input options … "jsx" Invalid
|
|
203
209
|
* key: Expected never but received "jsx"` on `nifra dev`.
|
|
204
210
|
*
|
|
205
211
|
* Why it happens: `@vitejs/plugin-react@4.x` (and peers) target an *older* rolldown-vite optimizeDeps
|
|
206
|
-
* API
|
|
212
|
+
* API - they inject `optimizeDeps.rollupOptions.jsx` to tell the dep pre-bundler to transform JSX. Vite
|
|
207
213
|
* 8's rolldown dep-optimizer renamed that surface to `optimizeDeps.rolldownOptions` (and moved jsx under
|
|
208
214
|
* `transform.jsx`), so the stale `rollupOptions.jsx` is an unrecognized input option → the warning. It's
|
|
209
215
|
* a version-skew artifact, not a real misconfig: the route source JSX transform runs through the
|
|
210
216
|
* plugin's own `transform` hook (untouched here), and node_modules deps that get pre-bundled almost
|
|
211
|
-
* never contain raw JSX
|
|
217
|
+
* never contain raw JSX - so dropping the dead key changes no behavior and keeps HMR/Fast Refresh
|
|
212
218
|
* intact. We *strip* (rather than translate to `rolldownOptions`) so the fix is version-agnostic: a
|
|
213
219
|
* plugin already emitting the correct `rolldownOptions` is left untouched, and a future plugin bump that
|
|
214
220
|
* stops emitting `rollupOptions.jsx` makes this a no-op.
|
|
@@ -216,12 +222,12 @@ export async function pipeWebBodyToNode(
|
|
|
216
222
|
* Scoped narrowly: only the `optimizeDeps.rollupOptions.jsx` key is removed, only under rolldown-vite,
|
|
217
223
|
* and only from the value a plugin's `config` hook returns. Non-rolldown Vite is passed through verbatim.
|
|
218
224
|
*
|
|
219
|
-
* FLATTEN FIRST: a Vite plugin factory may return an ARRAY of plugins
|
|
225
|
+
* FLATTEN FIRST: a Vite plugin factory may return an ARRAY of plugins - `@vitejs/plugin-react`'s `react()`
|
|
220
226
|
* returns `[vite:react-babel, vite:react-refresh]`, and it's `react:react-babel`'s `config` hook that emits
|
|
221
227
|
* the offending `optimizeDeps.rollupOptions.jsx`. `nifra.config.ts` writes `vitePlugins = [react()]`, so the
|
|
222
228
|
* plugin list arrives NESTED (`[[babel, refresh]]`). Without flattening, `.map` sees the inner array (which
|
|
223
|
-
* has no `config`), leaves it untouched, and Vite
|
|
224
|
-
*
|
|
229
|
+
* has no `config`), leaves it untouched, and Vite - which flattens plugin arrays itself before running them
|
|
230
|
+
* - then executes the un-stripped babel hook, so the warning survives. Flattening here (Vite accepts a flat
|
|
225
231
|
* list identically) is what lets the strip reach every real plugin.
|
|
226
232
|
*/
|
|
227
233
|
export function normalizeRolldownPlugins(
|
|
@@ -251,7 +257,7 @@ export function normalizeRolldownPlugins(
|
|
|
251
257
|
if (typeof handler !== "function") return plugin
|
|
252
258
|
const wrappedHandler: ConfigFn = (config, env) => {
|
|
253
259
|
const returned = handler(config, env)
|
|
254
|
-
// The hook may return a promise
|
|
260
|
+
// The hook may return a promise - normalize both shapes.
|
|
255
261
|
return returned instanceof Promise ? returned.then(stripJsxKey) : stripJsxKey(returned)
|
|
256
262
|
}
|
|
257
263
|
// Preserve the ORIGINAL shape: a function stays a function; the object form keeps its `order`
|
|
@@ -285,7 +291,7 @@ export async function createViteDevServer(options: ViteDevServerOptions): Promis
|
|
|
285
291
|
const entryUrl = `/${DEV_ENTRY}`
|
|
286
292
|
|
|
287
293
|
// 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)
|
|
294
|
+
// middleware mode Vite would otherwise open a *separate* ws port (24678) - fragile: it conflicts
|
|
289
295
|
// across restarts ("Port undefined is already in use" → the client never connects). Sharing one
|
|
290
296
|
// port means HMR rides the same origin as the app, robust across restarts. The handler closes over
|
|
291
297
|
// `vite`, which is assigned just below before the server starts listening.
|
|
@@ -302,7 +308,7 @@ export async function createViteDevServer(options: ViteDevServerOptions): Promis
|
|
|
302
308
|
const contentType = nifraRes.headers.get("content-type") ?? ""
|
|
303
309
|
res.statusCode = nifraRes.status
|
|
304
310
|
if (!contentType.includes("text/html")) {
|
|
305
|
-
// Data / redirect / asset response
|
|
311
|
+
// Data / redirect / asset response - pass through untouched, streamed (SSE-safe). Set-Cookie is
|
|
306
312
|
// emitted per-header so multi-cookie responses (e.g. better-auth sessions) aren't collapsed.
|
|
307
313
|
applyResponseHeaders(nifraRes.headers, res)
|
|
308
314
|
await pipeWebBodyToNode(nifraRes.body, res)
|
|
@@ -314,7 +320,7 @@ export async function createViteDevServer(options: ViteDevServerOptions): Promis
|
|
|
314
320
|
res.end(html)
|
|
315
321
|
} catch (err) {
|
|
316
322
|
// 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
|
|
323
|
+
// the readable dev overlay instead of a bare text dump. Dev-only - production maps to `_error`.
|
|
318
324
|
if (err instanceof Error) vite.ssrFixStacktrace(err)
|
|
319
325
|
res.statusCode = 500
|
|
320
326
|
res.setHeader("content-type", "text/html; charset=utf-8")
|
|
@@ -325,18 +331,22 @@ export async function createViteDevServer(options: ViteDevServerOptions): Promis
|
|
|
325
331
|
})
|
|
326
332
|
|
|
327
333
|
// `conditions: ["bun"]` makes Vite resolve nifra's workspace packages (`@nifrajs/web-react/client`, …) to
|
|
328
|
-
// their TS **source**
|
|
334
|
+
// their TS **source** - so the dev server needs no prior `dist` build of the adapter packages.
|
|
329
335
|
const usePolling = options.poll ?? process.env.CHOKIDAR_USEPOLLING === "1"
|
|
330
|
-
|
|
336
|
+
// Never `import("vite")` directly here: the guard in `importVite` has to run first, and the dev
|
|
337
|
+
// server importing vite unguarded is precisely what poisoned the module for the whole process.
|
|
338
|
+
const viteModule = await importVite<ViteModule>()
|
|
331
339
|
const { createServer } = viteModule
|
|
332
340
|
// Under rolldown-vite (Vite 8+), strip the stale `optimizeDeps.rollupOptions.jsx` some framework
|
|
333
|
-
// plugins still emit
|
|
341
|
+
// plugins still emit - it triggers a noisy "Invalid key … jsx" warning but does nothing useful here.
|
|
334
342
|
const plugins = normalizeRolldownPlugins(
|
|
335
343
|
options.plugins ?? [],
|
|
336
344
|
viteModule.rolldownVersion !== undefined,
|
|
337
345
|
)
|
|
338
346
|
vite = await createServer({
|
|
339
347
|
root,
|
|
348
|
+
// Dev must not inherit Vite's default `VITE_*` exposure beside Nifra's documented boundary.
|
|
349
|
+
envPrefix: vitePublicEnvPrefix(options.publicEnvPrefix),
|
|
340
350
|
...(options.publicDir !== undefined ? { publicDir: options.publicDir } : {}),
|
|
341
351
|
appType: "custom",
|
|
342
352
|
server: {
|
|
@@ -345,13 +355,15 @@ export async function createViteDevServer(options: ViteDevServerOptions): Promis
|
|
|
345
355
|
// Explicit watch config; poll when native fs events aren't delivered (containers/sandboxes).
|
|
346
356
|
watch: usePolling ? { usePolling: true, interval: 80 } : {},
|
|
347
357
|
},
|
|
348
|
-
plugins:
|
|
358
|
+
// Ahead of the user's plugins: a `*.fn` module must be replaced before anything else
|
|
359
|
+
// reads it, and the dev server is a client bundler like any other.
|
|
360
|
+
plugins: [viteServerFnStub(), viteServerOnlyEmpty(), ...plugins],
|
|
349
361
|
resolve: {
|
|
350
362
|
conditions: [...(options.conditions ?? []), "bun", "module", "browser", "development"],
|
|
351
363
|
// Dedupe React to ONE copy. In a multi-root workspace a shared package can pull react/react-dom
|
|
352
364
|
// from a SIBLING app's node_modules, so the dev server loads two React cores → a second hook
|
|
353
365
|
// dispatcher → `resolveDispatcher().useState` null on any hook-using route (the error points at the
|
|
354
|
-
// component, not the resolution
|
|
366
|
+
// component, not the resolution - brutal to diagnose). Mirrors the build-time reactDedupePlugin so
|
|
355
367
|
// dev matches prod. No-op for non-React apps (the package simply isn't present to dedupe).
|
|
356
368
|
dedupe: ["react", "react-dom"],
|
|
357
369
|
},
|