@nifrajs/web 3.5.0 → 4.0.2
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/dist/backend-only.d.ts +15 -0
- package/dist/backend-only.d.ts.map +1 -0
- package/dist/backend-only.js +16 -0
- package/dist/backend-only.js.map +1 -0
- package/dist/boundary.d.ts +5 -0
- package/dist/boundary.d.ts.map +1 -1
- package/dist/boundary.js +11 -2
- package/dist/boundary.js.map +1 -1
- package/dist/build-plan.d.ts +18 -6
- package/dist/build-plan.d.ts.map +1 -1
- package/dist/build-plan.js +82 -34
- package/dist/build-plan.js.map +1 -1
- package/dist/build-vite.d.ts +4 -0
- package/dist/build-vite.d.ts.map +1 -1
- package/dist/build-vite.js +68 -13
- package/dist/build-vite.js.map +1 -1
- package/dist/build.d.ts +57 -23
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +356 -85
- 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/cdn.d.ts +74 -0
- package/dist/cdn.d.ts.map +1 -0
- package/dist/cdn.js +132 -0
- package/dist/cdn.js.map +1 -0
- package/dist/client.d.ts +3 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +136 -35
- package/dist/client.js.map +1 -1
- package/dist/conformance.d.ts +4 -3
- package/dist/conformance.d.ts.map +1 -1
- package/dist/conformance.js +12 -3
- package/dist/conformance.js.map +1 -1
- package/dist/csp.d.ts +93 -0
- package/dist/csp.d.ts.map +1 -0
- package/dist/csp.js +122 -0
- package/dist/csp.js.map +1 -0
- package/dist/dev-client.d.ts +76 -0
- package/dist/dev-client.d.ts.map +1 -0
- package/dist/dev-client.js +280 -0
- package/dist/dev-client.js.map +1 -0
- package/dist/dev-diagnostics.d.ts +26 -2
- package/dist/dev-diagnostics.d.ts.map +1 -1
- package/dist/dev-diagnostics.js +38 -7
- package/dist/dev-diagnostics.js.map +1 -1
- package/dist/dev-error.d.ts +4 -13
- package/dist/dev-error.d.ts.map +1 -1
- package/dist/dev-error.js +46 -4
- package/dist/dev-error.js.map +1 -1
- package/dist/dev-feed.d.ts +298 -0
- package/dist/dev-feed.d.ts.map +1 -0
- package/dist/dev-feed.js +797 -0
- package/dist/dev-feed.js.map +1 -0
- package/dist/dev-indicator.d.ts +38 -0
- package/dist/dev-indicator.d.ts.map +1 -0
- package/dist/dev-indicator.js +93 -0
- package/dist/dev-indicator.js.map +1 -0
- package/dist/dev-session.d.ts +77 -0
- package/dist/dev-session.d.ts.map +1 -0
- package/dist/dev-session.js +580 -0
- package/dist/dev-session.js.map +1 -0
- package/dist/dev-sourcemap.d.ts +27 -0
- package/dist/dev-sourcemap.d.ts.map +1 -0
- package/dist/dev-sourcemap.js +235 -0
- package/dist/dev-sourcemap.js.map +1 -0
- package/dist/dev-ssr-graph.d.ts.map +1 -1
- package/dist/dev-ssr-graph.js +13 -5
- package/dist/dev-ssr-graph.js.map +1 -1
- package/dist/dev.d.ts +15 -2
- package/dist/dev.d.ts.map +1 -1
- package/dist/dev.js +69 -44
- package/dist/dev.js.map +1 -1
- package/dist/diagnostic-catalog.d.ts +35 -0
- package/dist/diagnostic-catalog.d.ts.map +1 -0
- package/dist/diagnostic-catalog.js +203 -0
- package/dist/diagnostic-catalog.js.map +1 -0
- package/dist/diagnostic-prompt.d.ts +49 -0
- package/dist/diagnostic-prompt.d.ts.map +1 -0
- package/dist/diagnostic-prompt.js +303 -0
- package/dist/diagnostic-prompt.js.map +1 -0
- package/dist/diagnostic.d.ts +17 -23
- package/dist/diagnostic.d.ts.map +1 -1
- package/dist/diagnostic.js +31 -39
- package/dist/diagnostic.js.map +1 -1
- package/dist/draft.d.ts.map +1 -1
- package/dist/draft.js +4 -1
- package/dist/draft.js.map +1 -1
- package/dist/fn.d.ts +5 -0
- package/dist/fn.d.ts.map +1 -1
- package/dist/fn.js +13 -3
- package/dist/fn.js.map +1 -1
- package/dist/fs.d.ts.map +1 -1
- package/dist/fs.js +4 -2
- package/dist/fs.js.map +1 -1
- package/dist/index.d.ts +13 -15
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -12
- package/dist/index.js.map +1 -1
- package/dist/internal/cdn/cloudflare.d.ts +48 -0
- package/dist/internal/cdn/cloudflare.d.ts.map +1 -0
- package/dist/internal/cdn/cloudflare.js +94 -0
- package/dist/internal/cdn/cloudflare.js.map +1 -0
- package/dist/internal/cdn/core.d.ts +91 -0
- package/dist/internal/cdn/core.d.ts.map +1 -0
- package/dist/internal/cdn/core.js +220 -0
- package/dist/internal/cdn/core.js.map +1 -0
- package/dist/internal/cdn/fastly.d.ts +17 -0
- package/dist/internal/cdn/fastly.d.ts.map +1 -0
- package/dist/internal/cdn/fastly.js +43 -0
- package/dist/internal/cdn/fastly.js.map +1 -0
- package/dist/internal/cdn/vercel.d.ts +31 -0
- package/dist/internal/cdn/vercel.d.ts.map +1 -0
- package/dist/internal/cdn/vercel.js +56 -0
- package/dist/internal/cdn/vercel.js.map +1 -0
- package/dist/internal/code-unit-order.d.ts +4 -0
- package/dist/internal/code-unit-order.d.ts.map +1 -0
- package/dist/internal/code-unit-order.js +6 -0
- package/dist/internal/code-unit-order.js.map +1 -0
- package/dist/internal/codegen.d.ts.map +1 -1
- package/dist/internal/codegen.js +141 -25
- package/dist/internal/codegen.js.map +1 -1
- package/dist/internal/dev-csp.d.ts +24 -0
- package/dist/internal/dev-csp.d.ts.map +1 -0
- package/dist/internal/dev-csp.js +160 -0
- package/dist/internal/dev-csp.js.map +1 -0
- package/dist/internal/dev-reserved.d.ts +25 -0
- package/dist/internal/dev-reserved.d.ts.map +1 -0
- package/dist/internal/dev-reserved.js +39 -0
- package/dist/internal/dev-reserved.js.map +1 -0
- package/dist/internal/html-spans.d.ts +10 -0
- package/dist/internal/html-spans.d.ts.map +1 -0
- package/dist/internal/html-spans.js +46 -0
- package/dist/internal/html-spans.js.map +1 -0
- package/dist/internal/loading-runtime.d.ts +44 -0
- package/dist/internal/loading-runtime.d.ts.map +1 -0
- package/dist/internal/loading-runtime.js +206 -0
- package/dist/internal/loading-runtime.js.map +1 -0
- package/dist/internal/matches-runtime.d.ts +8 -0
- package/dist/internal/matches-runtime.d.ts.map +1 -0
- package/dist/internal/matches-runtime.js +61 -0
- package/dist/internal/matches-runtime.js.map +1 -0
- package/dist/internal/mount-shadow.d.ts +36 -0
- package/dist/internal/mount-shadow.d.ts.map +1 -0
- package/dist/internal/mount-shadow.js +65 -0
- package/dist/internal/mount-shadow.js.map +1 -0
- package/dist/internal/node-builtins.d.ts +3 -0
- package/dist/internal/node-builtins.d.ts.map +1 -0
- package/dist/internal/node-builtins.js +79 -0
- package/dist/internal/node-builtins.js.map +1 -0
- package/dist/internal/output-guard.d.ts +65 -0
- package/dist/internal/output-guard.d.ts.map +1 -0
- package/dist/internal/output-guard.js +630 -0
- package/dist/internal/output-guard.js.map +1 -0
- package/dist/internal/page-execution.d.ts +20 -8
- package/dist/internal/page-execution.d.ts.map +1 -1
- package/dist/internal/page-execution.js +571 -105
- package/dist/internal/page-execution.js.map +1 -1
- package/dist/internal/parity.d.ts +21 -0
- package/dist/internal/parity.d.ts.map +1 -1
- package/dist/internal/parity.js +104 -22
- package/dist/internal/parity.js.map +1 -1
- package/dist/internal/private-env.d.ts +12 -0
- package/dist/internal/private-env.d.ts.map +1 -0
- package/dist/internal/private-env.js +335 -0
- package/dist/internal/private-env.js.map +1 -0
- package/dist/internal/public-url.d.ts +3 -0
- package/dist/internal/public-url.d.ts.map +1 -0
- package/dist/internal/public-url.js +18 -0
- package/dist/internal/public-url.js.map +1 -0
- package/dist/internal/render-document.d.ts +25 -19
- package/dist/internal/render-document.d.ts.map +1 -1
- package/dist/internal/render-document.js +77 -61
- package/dist/internal/render-document.js.map +1 -1
- package/dist/internal/response-controls.d.ts +75 -0
- package/dist/internal/response-controls.d.ts.map +1 -0
- package/dist/internal/response-controls.js +256 -0
- package/dist/internal/response-controls.js.map +1 -0
- package/dist/internal/runtime-contract.d.ts +11 -16
- package/dist/internal/runtime-contract.d.ts.map +1 -1
- package/dist/internal/runtime-contract.js.map +1 -1
- package/dist/internal/secret-scan.d.ts +104 -0
- package/dist/internal/secret-scan.d.ts.map +1 -0
- package/dist/internal/secret-scan.js +503 -0
- package/dist/internal/secret-scan.js.map +1 -0
- package/dist/internal/server-boundary.d.ts +6 -29
- package/dist/internal/server-boundary.d.ts.map +1 -1
- package/dist/internal/server-boundary.js +6 -66
- package/dist/internal/server-boundary.js.map +1 -1
- package/dist/internal/source-map.d.ts +47 -0
- package/dist/internal/source-map.d.ts.map +1 -0
- package/dist/internal/source-map.js +225 -0
- package/dist/internal/source-map.js.map +1 -0
- package/dist/internal/target-compat.d.ts +27 -0
- package/dist/internal/target-compat.d.ts.map +1 -0
- package/dist/internal/target-compat.js +77 -0
- package/dist/internal/target-compat.js.map +1 -0
- package/dist/internal/timing-safe-equal.d.ts +6 -0
- package/dist/internal/timing-safe-equal.d.ts.map +1 -1
- package/dist/internal/timing-safe-equal.js +10 -0
- package/dist/internal/timing-safe-equal.js.map +1 -1
- package/dist/internal/vitals-runtime.d.ts +18 -0
- package/dist/internal/vitals-runtime.d.ts.map +1 -0
- package/dist/internal/vitals-runtime.js +38 -0
- package/dist/internal/vitals-runtime.js.map +1 -0
- package/dist/internal/web-app.d.ts +38 -9
- package/dist/internal/web-app.d.ts.map +1 -1
- package/dist/internal/web-app.js +199 -59
- package/dist/internal/web-app.js.map +1 -1
- package/dist/internal/zone-graph.d.ts +89 -0
- package/dist/internal/zone-graph.d.ts.map +1 -0
- package/dist/internal/zone-graph.js +492 -0
- package/dist/internal/zone-graph.js.map +1 -0
- package/dist/isr.d.ts +83 -6
- package/dist/isr.d.ts.map +1 -1
- package/dist/isr.js +263 -36
- package/dist/isr.js.map +1 -1
- package/dist/llms-txt.d.ts.map +1 -1
- package/dist/llms-txt.js +31 -6
- package/dist/llms-txt.js.map +1 -1
- package/dist/manifest.d.ts +220 -13
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +394 -9
- package/dist/manifest.js.map +1 -1
- package/dist/module-graph.d.ts +26 -5
- package/dist/module-graph.d.ts.map +1 -1
- package/dist/module-graph.js +17 -7
- package/dist/module-graph.js.map +1 -1
- package/dist/navigation.d.ts +17 -3
- package/dist/navigation.d.ts.map +1 -1
- package/dist/navigation.js +11 -5
- package/dist/navigation.js.map +1 -1
- package/dist/nonce.d.ts +47 -0
- package/dist/nonce.d.ts.map +1 -0
- package/dist/nonce.js +63 -0
- package/dist/nonce.js.map +1 -0
- package/dist/plugins/css-modules.d.ts.map +1 -1
- package/dist/plugins/css-modules.js +4 -3
- package/dist/plugins/css-modules.js.map +1 -1
- package/dist/plugins/kit.d.ts +9 -0
- package/dist/plugins/kit.d.ts.map +1 -1
- package/dist/plugins/kit.js +18 -0
- package/dist/plugins/kit.js.map +1 -1
- package/dist/plugins/svg.d.ts +22 -3
- package/dist/plugins/svg.d.ts.map +1 -1
- package/dist/plugins/svg.js +112 -1
- package/dist/plugins/svg.js.map +1 -1
- package/dist/plugins/vite-leak-guard.d.ts +107 -3
- package/dist/plugins/vite-leak-guard.d.ts.map +1 -1
- package/dist/plugins/vite-leak-guard.js +378 -17
- package/dist/plugins/vite-leak-guard.js.map +1 -1
- package/dist/plugins/vite-zone-guard.d.ts +61 -0
- package/dist/plugins/vite-zone-guard.d.ts.map +1 -0
- package/dist/plugins/vite-zone-guard.js +208 -0
- package/dist/plugins/vite-zone-guard.js.map +1 -0
- package/dist/plugins/zone-guard.d.ts +27 -0
- package/dist/plugins/zone-guard.d.ts.map +1 -0
- package/dist/plugins/zone-guard.js +80 -0
- package/dist/plugins/zone-guard.js.map +1 -0
- package/dist/prerender.d.ts +10 -0
- package/dist/prerender.d.ts.map +1 -1
- package/dist/prerender.js +35 -3
- package/dist/prerender.js.map +1 -1
- package/dist/public-dir.d.ts +9 -0
- package/dist/public-dir.d.ts.map +1 -1
- package/dist/public-dir.js +67 -9
- package/dist/public-dir.js.map +1 -1
- package/dist/pwa-manifest.d.ts +68 -0
- package/dist/pwa-manifest.d.ts.map +1 -0
- package/dist/pwa-manifest.js +178 -0
- package/dist/pwa-manifest.js.map +1 -0
- package/dist/query.js +4 -4
- package/dist/query.js.map +1 -1
- package/dist/render-seam.d.ts +42 -2
- package/dist/render-seam.d.ts.map +1 -1
- package/dist/render-seam.js +6 -0
- package/dist/render-seam.js.map +1 -1
- package/dist/route-manifest.d.ts +3 -0
- package/dist/route-manifest.d.ts.map +1 -1
- package/dist/route-manifest.js +26 -3
- package/dist/route-manifest.js.map +1 -1
- package/dist/route-types.d.ts +24 -0
- package/dist/route-types.d.ts.map +1 -0
- package/dist/route-types.js +195 -0
- package/dist/route-types.js.map +1 -0
- package/dist/router.d.ts +28 -6
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +129 -40
- package/dist/router.js.map +1 -1
- package/dist/vitals.d.ts +33 -0
- package/dist/vitals.d.ts.map +1 -0
- package/dist/vitals.js +16 -0
- package/dist/vitals.js.map +1 -0
- package/dist/vite.d.ts +16 -1
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +248 -43
- package/dist/vite.js.map +1 -1
- package/dist/zones.d.ts +70 -0
- package/dist/zones.d.ts.map +1 -0
- package/dist/zones.js +365 -0
- package/dist/zones.js.map +1 -0
- package/package.json +67 -14
- package/src/backend-only.ts +16 -0
- package/src/boundary.ts +18 -5
- package/src/build-plan.ts +90 -35
- package/src/build-vite.ts +77 -11
- package/src/build.ts +466 -96
- package/src/bun-dev-entry.ts +1 -1
- package/src/cdn.ts +216 -0
- package/src/client.ts +135 -36
- package/src/conformance.ts +16 -3
- package/src/csp.ts +194 -0
- package/src/dev-client.ts +349 -0
- package/src/dev-diagnostics.ts +60 -7
- package/src/dev-error.ts +49 -5
- package/src/dev-feed.ts +1140 -0
- package/src/dev-indicator.ts +113 -0
- package/src/dev-session.ts +729 -0
- package/src/dev-sourcemap.ts +281 -0
- package/src/dev-ssr-graph.ts +16 -5
- package/src/dev.ts +86 -43
- package/src/diagnostic-catalog.ts +243 -0
- package/src/diagnostic-prompt.ts +355 -0
- package/src/diagnostic.ts +63 -66
- package/src/draft.ts +4 -1
- package/src/fn.ts +19 -3
- package/src/fs.ts +13 -2
- package/src/index.ts +42 -10
- package/src/internal/cdn/cloudflare.ts +146 -0
- package/src/internal/cdn/core.ts +316 -0
- package/src/internal/cdn/fastly.ts +62 -0
- package/src/internal/cdn/vercel.ts +95 -0
- package/src/internal/code-unit-order.ts +5 -0
- package/src/internal/codegen.ts +161 -24
- package/src/internal/dev-csp.ts +176 -0
- package/src/internal/dev-reserved.ts +52 -0
- package/src/internal/html-spans.ts +50 -0
- package/src/internal/loading-runtime.ts +244 -0
- package/src/internal/matches-runtime.ts +62 -0
- package/src/internal/mount-shadow.ts +88 -0
- package/src/internal/node-builtins.ts +79 -0
- package/src/internal/output-guard.ts +701 -0
- package/src/internal/page-execution.ts +795 -185
- package/src/internal/parity.ts +150 -36
- package/src/internal/private-env.ts +334 -0
- package/src/internal/public-url.ts +22 -0
- package/src/internal/render-document.ts +114 -67
- package/src/internal/response-controls.ts +284 -0
- package/src/internal/runtime-contract.ts +10 -15
- package/src/internal/secret-scan.ts +638 -0
- package/src/internal/server-boundary.ts +6 -69
- package/src/internal/source-map.ts +256 -0
- package/src/internal/target-compat.ts +92 -0
- package/src/internal/timing-safe-equal.ts +13 -0
- package/src/internal/vitals-runtime.ts +53 -0
- package/src/internal/web-app.ts +262 -84
- package/src/internal/zone-graph.ts +593 -0
- package/src/isr.ts +325 -37
- package/src/llms-txt.ts +28 -4
- package/src/manifest.ts +682 -16
- package/src/module-graph.ts +53 -9
- package/src/navigation.ts +29 -6
- package/src/nonce.ts +104 -0
- package/src/plugins/css-modules.ts +5 -3
- package/src/plugins/kit.ts +24 -0
- package/src/plugins/svg.ts +135 -4
- package/src/plugins/vite-leak-guard.ts +529 -16
- package/src/plugins/vite-zone-guard.ts +263 -0
- package/src/plugins/zone-guard.ts +119 -0
- package/src/prerender.ts +49 -3
- package/src/public-dir.ts +69 -8
- package/src/pwa-manifest.ts +226 -0
- package/src/query.ts +4 -4
- package/src/render-seam.ts +46 -1
- package/src/route-manifest.ts +15 -3
- package/src/route-types.ts +264 -0
- package/src/router.ts +163 -63
- package/src/vitals.ts +44 -0
- package/src/vite.ts +257 -42
- package/src/zones.ts +430 -0
- package/dist/plugins/vite-server-only.d.ts +0 -49
- package/dist/plugins/vite-server-only.d.ts.map +0 -1
- package/dist/plugins/vite-server-only.js +0 -57
- package/dist/plugins/vite-server-only.js.map +0 -1
- package/dist/server-only.d.ts +0 -21
- package/dist/server-only.d.ts.map +0 -1
- package/dist/server-only.js +0 -25
- package/dist/server-only.js.map +0 -1
- package/src/plugins/vite-server-only.ts +0 -75
- package/src/server-only.ts +0 -25
package/src/vite.ts
CHANGED
|
@@ -10,17 +10,20 @@
|
|
|
10
10
|
* injects its HMR client + the framework's refresh preamble. Node `http` (not `Bun.serve`) because
|
|
11
11
|
* Vite's `middlewares` are Connect-style - it runs fine under Bun.
|
|
12
12
|
*/
|
|
13
|
-
import { writeFileSync } from "node:fs"
|
|
13
|
+
import { type Dirent, readdirSync, writeFileSync } from "node:fs"
|
|
14
14
|
import {
|
|
15
15
|
createServer as createHttpServer,
|
|
16
16
|
type IncomingMessage,
|
|
17
17
|
type ServerResponse,
|
|
18
18
|
} from "node:http"
|
|
19
|
-
import { isAbsolute, join, relative, resolve as resolvePath, sep } from "node:path"
|
|
20
|
-
import {
|
|
19
|
+
import { dirname, isAbsolute, join, relative, resolve as resolvePath, sep } from "node:path"
|
|
20
|
+
import { CLIENT_BATCH_MAX_BYTES } from "./dev-client.ts"
|
|
21
|
+
import { DEV_REQUEST_ID_HEADER, DEV_SERVER_FILES } from "./dev-feed.ts"
|
|
21
22
|
import { listenOrExplain } from "./dev-port.ts"
|
|
23
|
+
import { createDevSession, type DevAppHooks, isDevAgentPath } from "./dev-session.ts"
|
|
22
24
|
import { discoverRoutes } from "./fs.ts"
|
|
23
25
|
import { DEFAULT_DEV_PORT, generateClientEntry, setSsrModuleLoader } from "./index.ts"
|
|
26
|
+
import { admitViteTags } from "./internal/dev-csp.ts"
|
|
24
27
|
import { viteDedupePackages } from "./internal/identity-policy.ts"
|
|
25
28
|
import {
|
|
26
29
|
assertIdentityParity,
|
|
@@ -39,7 +42,7 @@ import {
|
|
|
39
42
|
reproduciblePath,
|
|
40
43
|
} from "./plugins/kit.ts"
|
|
41
44
|
import { viteServerFnStub } from "./plugins/vite-server-fn.ts"
|
|
42
|
-
import {
|
|
45
|
+
import { viteZoneGuard } from "./plugins/vite-zone-guard.ts"
|
|
43
46
|
|
|
44
47
|
/** Minimal app surface - `createWebApp(...)` satisfies it. */
|
|
45
48
|
interface FetchApp {
|
|
@@ -62,13 +65,20 @@ interface ViteModuleGraph {
|
|
|
62
65
|
interface ViteLike {
|
|
63
66
|
/** Load a module through VITE's graph - the seam that makes the Vite pipeline own SSR too. */
|
|
64
67
|
ssrLoadModule(url: string): Promise<Record<string, unknown>>
|
|
65
|
-
readonly middlewares: (
|
|
68
|
+
readonly middlewares: (
|
|
69
|
+
req: IncomingMessage,
|
|
70
|
+
res: ServerResponse,
|
|
71
|
+
next: (error?: unknown) => void,
|
|
72
|
+
) => void
|
|
66
73
|
transformIndexHtml(url: string, html: string): Promise<string>
|
|
67
74
|
ssrFixStacktrace(err: Error): void
|
|
68
75
|
/** The module graph, used to invalidate a changed file AND its transitive importers before reload. */
|
|
69
76
|
readonly moduleGraph?: ViteModuleGraph
|
|
70
77
|
readonly watcher: {
|
|
71
78
|
on(event: "change" | "add" | "unlink", cb: (path: string) => void): void
|
|
79
|
+
on(event: "ready", cb: () => void): void
|
|
80
|
+
/** Chokidar's ready state, used to handle a ready event emitted before this adapter attaches. */
|
|
81
|
+
readonly _readyEmitted?: boolean
|
|
72
82
|
}
|
|
73
83
|
close(): Promise<void>
|
|
74
84
|
}
|
|
@@ -94,10 +104,14 @@ export interface ViteDevServerOptions {
|
|
|
94
104
|
* Without it SSR resolves through Bun while the client resolves through Vite - two resolvers, one
|
|
95
105
|
* process - which is what makes `resolve.dedupe` fail to reach SSR and produces the dual-React
|
|
96
106
|
* crash. Vite re-evaluates on change, so no `importQuery` cache-buster is needed alongside it.
|
|
107
|
+
*
|
|
108
|
+
* Pass `dev.onLoaderError` to `createWebApp` so failures an `_error` boundary renders still reach the
|
|
109
|
+
* dev feed (`nifra errors`).
|
|
97
110
|
*/
|
|
98
111
|
readonly createApp: (
|
|
99
112
|
clientEntry: string,
|
|
100
113
|
load: (absolutePath: string) => Promise<unknown>,
|
|
114
|
+
dev: DevAppHooks,
|
|
101
115
|
) => FetchApp | Promise<FetchApp>
|
|
102
116
|
/** Vite plugins - inject your framework's official plugin, e.g. `[react()]`. */
|
|
103
117
|
readonly plugins?: readonly unknown[]
|
|
@@ -133,6 +147,16 @@ export interface ViteDevServerOptions {
|
|
|
133
147
|
* resolve it; `nifra build` never honors it. Wired to `nifra dev --allow-duplicate-identity`.
|
|
134
148
|
*/
|
|
135
149
|
readonly allowDuplicateIdentity?: boolean
|
|
150
|
+
/**
|
|
151
|
+
* Write `.nifra/dev-server.json` (how `nifra errors`/`nifra logs` and the MCP tools find this server)
|
|
152
|
+
* and the persisted dev log beside it (default `true`).
|
|
153
|
+
*/
|
|
154
|
+
readonly record?: boolean
|
|
155
|
+
/**
|
|
156
|
+
* Show the browser errors a dev page reports in a badge on that page, with a Copy prompt button per
|
|
157
|
+
* fix (default `true`). Off, the errors still reach the feed.
|
|
158
|
+
*/
|
|
159
|
+
readonly indicator?: boolean
|
|
136
160
|
}
|
|
137
161
|
|
|
138
162
|
export interface ViteDevServer {
|
|
@@ -140,10 +164,13 @@ export interface ViteDevServer {
|
|
|
140
164
|
stop(): Promise<void>
|
|
141
165
|
}
|
|
142
166
|
|
|
167
|
+
export type { DevAppHooks } from "./dev-session.ts"
|
|
143
168
|
export { LAST_ERROR_PATH } from "./diagnostic.ts"
|
|
144
169
|
|
|
145
170
|
// The codegen'd client entry is written here (at the Vite root) so Vite serves + HMRs it.
|
|
146
171
|
const DEV_ENTRY = ".nifra-vite-entry.tsx"
|
|
172
|
+
const VITE_CLOSE_BOUND_MS = 2000
|
|
173
|
+
const POLL_INTERVAL_MS = 80
|
|
147
174
|
|
|
148
175
|
/** Candidate spellings for Vite's module-graph map. Vite has used native and slash-normalized
|
|
149
176
|
* absolute Windows paths across versions, while watcher events can additionally arrive as a file URL. */
|
|
@@ -158,6 +185,22 @@ const pathInside = (root: string, path: string): boolean => {
|
|
|
158
185
|
return rel === "" || (rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel))
|
|
159
186
|
}
|
|
160
187
|
|
|
188
|
+
/** Every file below `dir` as an absolute path. A directory removed mid-walk contributes nothing. */
|
|
189
|
+
const filesUnder = (dir: string, into = new Set<string>()): Set<string> => {
|
|
190
|
+
let entries: Dirent[]
|
|
191
|
+
try {
|
|
192
|
+
entries = readdirSync(dir, { withFileTypes: true })
|
|
193
|
+
} catch {
|
|
194
|
+
return into
|
|
195
|
+
}
|
|
196
|
+
for (const entry of entries) {
|
|
197
|
+
const path = join(dir, entry.name)
|
|
198
|
+
if (entry.isDirectory()) filesUnder(path, into)
|
|
199
|
+
else into.add(path)
|
|
200
|
+
}
|
|
201
|
+
return into
|
|
202
|
+
}
|
|
203
|
+
|
|
161
204
|
const readNodeBody = async (req: IncomingMessage): Promise<Buffer | undefined> => {
|
|
162
205
|
if (req.method === "GET" || req.method === "HEAD") return undefined
|
|
163
206
|
const chunks: Buffer[] = []
|
|
@@ -165,6 +208,23 @@ const readNodeBody = async (req: IncomingMessage): Promise<Buffer | undefined> =
|
|
|
165
208
|
return Buffer.concat(chunks)
|
|
166
209
|
}
|
|
167
210
|
|
|
211
|
+
/**
|
|
212
|
+
* An agent request's body, never buffered past `max` bytes. A larger body is drained, not kept, so the
|
|
213
|
+
* client still gets an answer: the session refuses the truncated batch.
|
|
214
|
+
*/
|
|
215
|
+
const readAgentBody = async (req: IncomingMessage, max: number): Promise<Buffer | undefined> => {
|
|
216
|
+
if (req.method === "GET" || req.method === "HEAD") return undefined
|
|
217
|
+
const chunks: Buffer[] = []
|
|
218
|
+
let total = 0
|
|
219
|
+
for await (const chunk of req) {
|
|
220
|
+
if (!Buffer.isBuffer(chunk)) continue
|
|
221
|
+
total += chunk.length
|
|
222
|
+
if (total <= max) chunks.push(chunk)
|
|
223
|
+
}
|
|
224
|
+
// One byte past the cap is enough for the session's own size check to refuse it.
|
|
225
|
+
return total > max ? Buffer.alloc(max + 1) : Buffer.concat(chunks)
|
|
226
|
+
}
|
|
227
|
+
|
|
168
228
|
/** Build a Web `Request` from a Node `IncomingMessage` (+ already-read body) for nifra's `app.fetch`. */
|
|
169
229
|
function toWebRequest(req: IncomingMessage, body: Buffer | undefined): Request {
|
|
170
230
|
const headers = new Headers()
|
|
@@ -372,6 +432,9 @@ export async function createViteDevServer(options: ViteDevServerOptions): Promis
|
|
|
372
432
|
generateClientEntry(manifest, { clientModule: options.clientModule, resolve: toUrl }),
|
|
373
433
|
)
|
|
374
434
|
}
|
|
435
|
+
const usePolling = options.poll ?? process.env.CHOKIDAR_USEPOLLING === "1"
|
|
436
|
+
// Listed before the entry is generated, so a file added in between is reconciled again, not lost.
|
|
437
|
+
const routeFiles = usePolling ? filesUnder(routesDir) : new Set<string>()
|
|
375
438
|
writeClientEntry()
|
|
376
439
|
const entryUrl = `/${DEV_ENTRY}`
|
|
377
440
|
|
|
@@ -382,27 +445,101 @@ export async function createViteDevServer(options: ViteDevServerOptions): Promis
|
|
|
382
445
|
// `vite`, which is assigned just below before the server starts listening.
|
|
383
446
|
let vite!: ViteLike
|
|
384
447
|
let app: FetchApp
|
|
385
|
-
//
|
|
386
|
-
//
|
|
387
|
-
const
|
|
448
|
+
// Failures, console output, request traces and the discovery record for the agent driving this
|
|
449
|
+
// server, plus the overlay and LAST_ERROR_PATH. Shared with the Bun adapter so they cannot drift.
|
|
450
|
+
const session = createDevSession({
|
|
451
|
+
root,
|
|
452
|
+
pipeline: "vite",
|
|
453
|
+
publicEnvPrefix: options.publicEnvPrefix,
|
|
454
|
+
record: options.record,
|
|
455
|
+
indicator: options.indicator,
|
|
456
|
+
})
|
|
457
|
+
const devHooks: DevAppHooks = { onLoaderError: session.onLoaderError }
|
|
458
|
+
|
|
459
|
+
// Refuses backend code before Vite serves or transforms it; held here so a refused module's request
|
|
460
|
+
// is answered with the refusal rather than falling through to the app.
|
|
461
|
+
const zoneGuard = viteZoneGuard({
|
|
462
|
+
appRoot: dirname(routesDir),
|
|
463
|
+
routesDir,
|
|
464
|
+
generatedFiles: [resolvePath(root, DEV_ENTRY)],
|
|
465
|
+
...(options.publicEnvPrefix !== undefined ? { publicEnvPrefix: options.publicEnvPrefix } : {}),
|
|
466
|
+
})
|
|
388
467
|
|
|
389
468
|
const server: NodeHttpServer = createHttpServer((req, res) => {
|
|
390
|
-
// Keep this before Vite's middleware:
|
|
391
|
-
if (
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
469
|
+
// Keep this before Vite's middleware: the agent endpoints are owned by nifra, not files or routes.
|
|
470
|
+
if (isDevAgentPath((req.url ?? "/").split("?", 1)[0] ?? "/")) {
|
|
471
|
+
void (async () => {
|
|
472
|
+
try {
|
|
473
|
+
const body = await readAgentBody(req, CLIENT_BATCH_MAX_BYTES)
|
|
474
|
+
const agent = await session.handle(toWebRequest(req, body))
|
|
475
|
+
if (agent === undefined) {
|
|
476
|
+
handleWithVite(req, res)
|
|
477
|
+
return
|
|
478
|
+
}
|
|
479
|
+
res.statusCode = agent.status
|
|
480
|
+
applyResponseHeaders(agent.headers, res)
|
|
481
|
+
res.end(await agent.text())
|
|
482
|
+
} catch (err) {
|
|
483
|
+
// A client that aborts mid-body rejects the read; unhandled, that rejection ends the process.
|
|
484
|
+
if (!req.destroyed) console.error("[nifra/web/vite] dev endpoint failed:", err)
|
|
485
|
+
if (!res.headersSent) res.statusCode = 500
|
|
486
|
+
res.end()
|
|
487
|
+
}
|
|
488
|
+
})()
|
|
396
489
|
return
|
|
397
490
|
}
|
|
398
|
-
|
|
491
|
+
handleWithVite(req, res)
|
|
492
|
+
})
|
|
493
|
+
|
|
494
|
+
function handleWithVite(req: IncomingMessage, res: ServerResponse): void {
|
|
495
|
+
vite.middlewares(req, res, (error) => {
|
|
496
|
+
// In middleware mode Vite reports a module's transform error to the overlay and passes the
|
|
497
|
+
// request on. A request naming a source file was for a module, never a page: it gets the error,
|
|
498
|
+
// not the app's HTML.
|
|
499
|
+
const url = req.url ?? "/"
|
|
500
|
+
const file = /\.[\w]+(?:[?#]|$)/.test(url) ? zoneGuard.fileForUrl(url) : undefined
|
|
501
|
+
if (error !== undefined || file !== undefined) {
|
|
502
|
+
res.statusCode = 500
|
|
503
|
+
res.setHeader("content-type", "text/plain; charset=utf-8")
|
|
504
|
+
res.setHeader("cache-control", "no-store")
|
|
505
|
+
const reason =
|
|
506
|
+
zoneGuard.refusalFor(url) ??
|
|
507
|
+
(error instanceof Error
|
|
508
|
+
? error.message
|
|
509
|
+
: `[nifra] ${url} failed to transform; the dev server log has the error`)
|
|
510
|
+
session.buildFailed(error ?? new Error(reason), `${url} failed to transform`)
|
|
511
|
+
res.end(`${reason}\n`)
|
|
512
|
+
return
|
|
513
|
+
}
|
|
399
514
|
// Not a Vite asset → nifra SSR. (`next` runs after Vite declines, so the body is still readable.)
|
|
400
515
|
void (async () => {
|
|
401
516
|
try {
|
|
402
517
|
const body = await readNodeBody(req)
|
|
403
|
-
const
|
|
518
|
+
const webRequest = toWebRequest(req, body)
|
|
519
|
+
let overlay = false
|
|
520
|
+
const nifraRes = await session.track(webRequest, async () => {
|
|
521
|
+
try {
|
|
522
|
+
return await app.fetch(webRequest)
|
|
523
|
+
} catch (err) {
|
|
524
|
+
// Source-map the stack first (Vite maps the bundled frames back to your `.ts`), then render
|
|
525
|
+
// the readable dev overlay instead of a bare text dump. Dev-only - production maps to `_error`.
|
|
526
|
+
if (err instanceof Error) vite.ssrFixStacktrace(err)
|
|
527
|
+
overlay = true
|
|
528
|
+
return new Response(
|
|
529
|
+
session.failure(err, { method: req.method ?? "GET", url: req.url ?? "/" }),
|
|
530
|
+
{ status: 500, headers: { "content-type": "text/html; charset=utf-8" } },
|
|
531
|
+
)
|
|
532
|
+
}
|
|
533
|
+
})
|
|
404
534
|
const contentType = nifraRes.headers.get("content-type") ?? ""
|
|
405
535
|
res.statusCode = nifraRes.status
|
|
536
|
+
const requestId = nifraRes.headers.get(DEV_REQUEST_ID_HEADER)
|
|
537
|
+
if (requestId !== null) res.setHeader(DEV_REQUEST_ID_HEADER, requestId)
|
|
538
|
+
if (overlay || session.isOverlay(nifraRes)) {
|
|
539
|
+
res.setHeader("content-type", "text/html; charset=utf-8")
|
|
540
|
+
res.end(await nifraRes.text())
|
|
541
|
+
return
|
|
542
|
+
}
|
|
406
543
|
if (!contentType.includes("text/html")) {
|
|
407
544
|
// Data / redirect / asset response - pass through untouched, streamed (SSE-safe). Set-Cookie is
|
|
408
545
|
// emitted per-header so multi-cookie responses (e.g. better-auth sessions) aren't collapsed.
|
|
@@ -410,25 +547,26 @@ export async function createViteDevServer(options: ViteDevServerOptions): Promis
|
|
|
410
547
|
await pipeWebBodyToNode(nifraRes.body, res)
|
|
411
548
|
return
|
|
412
549
|
}
|
|
413
|
-
// Inject Vite's HMR client + the framework's refresh preamble into the SSR'd HTML
|
|
414
|
-
|
|
550
|
+
// Inject Vite's HMR client + the framework's refresh preamble into the SSR'd HTML, keeping the
|
|
551
|
+
// app's headers (cookies, CSP, cache-control) as the Bun pipeline does.
|
|
552
|
+
const headers = new Headers(nifraRes.headers)
|
|
553
|
+
headers.delete("content-length") // the body grows with Vite's tags
|
|
554
|
+
const page = session.decorateHtml(webRequest, await nifraRes.text(), headers, requestId)
|
|
555
|
+
const html = admitViteTags(await vite.transformIndexHtml(req.url ?? "/", page), headers)
|
|
556
|
+
applyResponseHeaders(headers, res)
|
|
415
557
|
res.setHeader("content-type", "text/html; charset=utf-8")
|
|
416
558
|
res.end(html)
|
|
417
559
|
} catch (err) {
|
|
418
|
-
//
|
|
419
|
-
// the readable dev overlay instead of a bare text dump. Dev-only - production maps to `_error`.
|
|
560
|
+
// A failure past the app (reading the body, Vite's HTML transform): same overlay, same feed.
|
|
420
561
|
if (err instanceof Error) vite.ssrFixStacktrace(err)
|
|
421
|
-
const html =
|
|
422
|
-
method: req.method ?? "GET",
|
|
423
|
-
url: req.url ?? "/",
|
|
424
|
-
})
|
|
562
|
+
const html = session.failure(err, { method: req.method ?? "GET", url: req.url ?? "/" })
|
|
425
563
|
res.statusCode = 500
|
|
426
564
|
res.setHeader("content-type", "text/html; charset=utf-8")
|
|
427
565
|
res.end(html)
|
|
428
566
|
}
|
|
429
567
|
})()
|
|
430
568
|
})
|
|
431
|
-
}
|
|
569
|
+
}
|
|
432
570
|
|
|
433
571
|
// `conditions: ["bun"]` makes Vite resolve nifra's workspace packages (`@nifrajs/web-react/client`, …) to
|
|
434
572
|
// their TS **source** - so the dev server needs no prior `dist` build of the adapter packages.
|
|
@@ -460,7 +598,16 @@ export async function createViteDevServer(options: ViteDevServerOptions): Promis
|
|
|
460
598
|
// path-shaped `clientModule` (tests) names no package; there is then nothing to externalize.
|
|
461
599
|
const adapterPackage = packageNameOf(options.clientModule)
|
|
462
600
|
const ssrExternal = [...(adapterPackage !== undefined ? [adapterPackage] : []), "@nifrajs/web"]
|
|
463
|
-
|
|
601
|
+
// The feed writes its record and log under `.nifra/`: watching them would turn every log flush into
|
|
602
|
+
// a "file change" that re-creates the app and marks every entry stale.
|
|
603
|
+
const watch: { ignored: RegExp[]; usePolling?: boolean; interval?: number } = {
|
|
604
|
+
ignored: [DEV_SERVER_FILES],
|
|
605
|
+
}
|
|
606
|
+
// Poll when native fs events aren't delivered (containers/sandboxes).
|
|
607
|
+
if (usePolling) {
|
|
608
|
+
watch.usePolling = true
|
|
609
|
+
watch.interval = POLL_INTERVAL_MS
|
|
610
|
+
}
|
|
464
611
|
// Never `import("vite")` directly here: the guard in `importVite` has to run first, and the dev
|
|
465
612
|
// server importing vite unguarded is precisely what poisoned the module for the whole process.
|
|
466
613
|
const viteModule = await importVite<ViteModule>()
|
|
@@ -480,12 +627,11 @@ export async function createViteDevServer(options: ViteDevServerOptions): Promis
|
|
|
480
627
|
server: {
|
|
481
628
|
middlewareMode: true,
|
|
482
629
|
hmr: { server },
|
|
483
|
-
|
|
484
|
-
watch: usePolling ? { usePolling: true, interval: 80 } : {},
|
|
630
|
+
watch,
|
|
485
631
|
},
|
|
486
|
-
// Ahead of the user's plugins:
|
|
487
|
-
//
|
|
488
|
-
plugins: [
|
|
632
|
+
// Ahead of the user's plugins: the zone guard refuses backend code before anything serves or
|
|
633
|
+
// transforms it, and a `*.fn` module must be replaced before anything else reads it.
|
|
634
|
+
plugins: [zoneGuard, viteServerFnStub(), ...plugins],
|
|
489
635
|
resolve: {
|
|
490
636
|
conditions: resolveConditions,
|
|
491
637
|
// Dedupe each framework runtime to ONE copy. In a multi-root workspace a shared package can pull
|
|
@@ -523,6 +669,22 @@ export async function createViteDevServer(options: ViteDevServerOptions): Promis
|
|
|
523
669
|
},
|
|
524
670
|
...(options.define ? { define: options.define } : {}),
|
|
525
671
|
})
|
|
672
|
+
// Vite resolves before chokidar necessarily finishes its initial scan. Do not expose the
|
|
673
|
+
// dev server until that scan is complete: a route created immediately after startup must be
|
|
674
|
+
// observed as a change, not folded into the ignored initial snapshot on slower filesystems.
|
|
675
|
+
const watcherReady = new Promise<void>((resolve) => {
|
|
676
|
+
let settled = false
|
|
677
|
+
const markReady = (): void => {
|
|
678
|
+
if (settled) return
|
|
679
|
+
settled = true
|
|
680
|
+
resolve()
|
|
681
|
+
}
|
|
682
|
+
vite.watcher.on("ready", markReady)
|
|
683
|
+
// createServer() can return after chokidar has emitted `ready`, especially on fast local
|
|
684
|
+
// filesystems. Chokidar exposes this state in its structural type so we can cover that case
|
|
685
|
+
// without a startup timeout or a false-positive partial directory snapshot.
|
|
686
|
+
if (vite.watcher._readyEmitted === true) markReady()
|
|
687
|
+
})
|
|
526
688
|
|
|
527
689
|
const ssrLoad = (absolutePath: string): Promise<unknown> => vite.ssrLoadModule(absolutePath)
|
|
528
690
|
// Published so an ADAPTER can reach this graph too. Route modules already load through it; an
|
|
@@ -530,7 +692,7 @@ export async function createViteDevServer(options: ViteDevServerOptions): Promis
|
|
|
530
692
|
// `Chain.svelte`) would otherwise take it from the runtime, which has no compiler for it and no
|
|
531
693
|
// reason to agree with Vite about which copy of the framework runtime it renders through.
|
|
532
694
|
setSsrModuleLoader(ssrLoad)
|
|
533
|
-
app = await options.createApp(entryUrl, ssrLoad)
|
|
695
|
+
app = await options.createApp(entryUrl, ssrLoad, devHooks)
|
|
534
696
|
|
|
535
697
|
// Evict a changed file from the SSR graph together with every module that (transitively) imports it,
|
|
536
698
|
// BEFORE re-creating the app. Vite re-evaluates a directly-changed module on its own, but a parent
|
|
@@ -566,20 +728,58 @@ export async function createViteDevServer(options: ViteDevServerOptions): Promis
|
|
|
566
728
|
const refreshApp = (path?: string): void => {
|
|
567
729
|
if (path !== undefined) invalidateImporterClosure(path)
|
|
568
730
|
const version = ++refreshVersion
|
|
569
|
-
Promise.resolve(options.createApp(entryUrl, ssrLoad))
|
|
731
|
+
Promise.resolve(options.createApp(entryUrl, ssrLoad, devHooks))
|
|
570
732
|
.then((next) => {
|
|
571
733
|
if (version === refreshVersion) app = next
|
|
572
734
|
})
|
|
573
|
-
.catch((err) =>
|
|
735
|
+
.catch((err) => {
|
|
736
|
+
console.error("[nifra/web/vite] app re-create failed:", err)
|
|
737
|
+
session.buildFailed(err, "app re-create failed")
|
|
738
|
+
})
|
|
739
|
+
}
|
|
740
|
+
vite.watcher.on("change", (path) => {
|
|
741
|
+
session.markChange()
|
|
742
|
+
refreshApp(normalizeFilePath(path))
|
|
743
|
+
})
|
|
744
|
+
const onAddUnlink = (event: "add" | "unlink", path: string): void => {
|
|
745
|
+
const filePath = normalizeFilePath(path)
|
|
746
|
+
const inRoutes = pathInside(routesDir, filePath)
|
|
747
|
+
if (inRoutes && usePolling) {
|
|
748
|
+
const file = resolvePath(filePath)
|
|
749
|
+
// Whichever of the watcher and the rescan below reports a route file first handles it.
|
|
750
|
+
if (routeFiles.has(file) === (event === "add")) return
|
|
751
|
+
if (event === "add") routeFiles.add(file)
|
|
752
|
+
else routeFiles.delete(file)
|
|
753
|
+
}
|
|
754
|
+
session.markChange()
|
|
755
|
+
if (inRoutes) {
|
|
756
|
+
writeClientEntry()
|
|
757
|
+
// A newly added route is not in Vite's module graph yet, so invalidating only the
|
|
758
|
+
// route path cannot evict the cached generated entry that imports the route. Evict the
|
|
759
|
+
// generated entry after rewriting it before recreating the SSR app; this also prevents
|
|
760
|
+
// an unlink refresh from briefly loading an entry that still references the deleted file.
|
|
761
|
+
invalidateImporterClosure(resolvePath(root, DEV_ENTRY))
|
|
762
|
+
}
|
|
763
|
+
refreshApp(filePath)
|
|
574
764
|
}
|
|
575
|
-
vite.watcher.on("change", (path) => refreshApp(normalizeFilePath(path)))
|
|
576
765
|
for (const event of ["add", "unlink"] as const) {
|
|
577
|
-
vite.watcher.on(event, (path) =>
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
766
|
+
vite.watcher.on(event, (path) => onAddUnlink(event, path))
|
|
767
|
+
}
|
|
768
|
+
// Chokidar reports ready before the poller has taken its baseline stat of a directory (Bun's
|
|
769
|
+
// fs.watchFile takes it asynchronously), and a route added or removed in that gap is folded into the
|
|
770
|
+
// baseline and never reported. The manifest comes from a directory scan, so a scan diffed against the
|
|
771
|
+
// route files already handled recovers those events.
|
|
772
|
+
const reconcileRouteFiles = (): void => {
|
|
773
|
+
const onDisk = filesUnder(routesDir)
|
|
774
|
+
try {
|
|
775
|
+
for (const file of onDisk) if (!routeFiles.has(file)) onAddUnlink("add", file)
|
|
776
|
+
for (const file of [...routeFiles]) if (!onDisk.has(file)) onAddUnlink("unlink", file)
|
|
777
|
+
} catch (err) {
|
|
778
|
+
console.error("[nifra/web/vite] route rescan failed:", err)
|
|
779
|
+
session.buildFailed(err, "route rescan failed")
|
|
780
|
+
}
|
|
582
781
|
}
|
|
782
|
+
await watcherReady
|
|
583
783
|
|
|
584
784
|
try {
|
|
585
785
|
await listenOrExplain(server, port, "127.0.0.1")
|
|
@@ -589,21 +789,36 @@ export async function createViteDevServer(options: ViteDevServerOptions): Promis
|
|
|
589
789
|
// then HANGS on it, which is worse than the raw crash this guard replaced: the user sees a dev server
|
|
590
790
|
// that appears to be starting. Tear Vite down so the failure is terminal.
|
|
591
791
|
await vite.close().catch(() => {})
|
|
792
|
+
session.stop()
|
|
592
793
|
throw err
|
|
593
794
|
}
|
|
594
795
|
// Report the port actually BOUND, not the one requested. They differ for `port: 0`, which is how you
|
|
595
796
|
// ask the OS for a free one - the correct thing to do in a test or when running several apps at once.
|
|
596
797
|
// Echoing the request back would return a literal 0, which connects to nothing.
|
|
597
798
|
const address = server.address()
|
|
799
|
+
const boundPort = typeof address === "object" && address !== null ? address.port : port
|
|
800
|
+
session.listening(boundPort)
|
|
801
|
+
const routeRescan = usePolling ? setInterval(reconcileRouteFiles, POLL_INTERVAL_MS) : undefined
|
|
598
802
|
return {
|
|
599
|
-
port:
|
|
803
|
+
port: boundPort,
|
|
600
804
|
stop: async () => {
|
|
805
|
+
clearInterval(routeRescan)
|
|
806
|
+
session.stop()
|
|
601
807
|
// Cleared with the server that owns it: the slot is process-global, so a stopped server leaving
|
|
602
808
|
// its loader behind would hand the next one's adapter a graph that is closed (tests start and
|
|
603
809
|
// stop several dev servers in one process).
|
|
604
810
|
setSsrModuleLoader(undefined)
|
|
605
811
|
server.close()
|
|
606
|
-
|
|
812
|
+
// Vite (8.2) never settles a first dependency optimization that close() cuts short, and close()
|
|
813
|
+
// waits on it for good. Everything else closes in parallel, so only that wait is bounded.
|
|
814
|
+
let bound: ReturnType<typeof setTimeout> | undefined
|
|
815
|
+
await Promise.race([
|
|
816
|
+
vite.close(),
|
|
817
|
+
new Promise<void>((resolve) => {
|
|
818
|
+
bound = setTimeout(resolve, VITE_CLOSE_BOUND_MS)
|
|
819
|
+
}),
|
|
820
|
+
])
|
|
821
|
+
clearTimeout(bound)
|
|
607
822
|
},
|
|
608
823
|
}
|
|
609
824
|
}
|