@textjs/app 0.0.0-stage → 1.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/README.md +34 -2
- package/bin/text.js +29 -0
- package/bin/text.ts +13 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/cli/build.d.ts +39 -0
- package/dist/cli/build.d.ts.map +1 -0
- package/dist/cli/build.js +157 -0
- package/dist/cli/build.js.map +1 -0
- package/dist/cli/config.d.ts +22 -0
- package/dist/cli/config.d.ts.map +1 -0
- package/dist/cli/config.js +73 -0
- package/dist/cli/config.js.map +1 -0
- package/dist/cli/dev.d.ts +12 -0
- package/dist/cli/dev.d.ts.map +1 -0
- package/dist/cli/dev.js +49 -0
- package/dist/cli/dev.js.map +1 -0
- package/dist/cli/index.d.ts +26 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +192 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/run.d.ts +31 -0
- package/dist/cli/run.d.ts.map +1 -0
- package/dist/cli/run.js +111 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/cli/start.d.ts +14 -0
- package/dist/cli/start.d.ts.map +1 -0
- package/dist/cli/start.js +183 -0
- package/dist/cli/start.js.map +1 -0
- package/dist/cli/ui.d.ts +35 -0
- package/dist/cli/ui.d.ts.map +1 -0
- package/dist/cli/ui.js +73 -0
- package/dist/cli/ui.js.map +1 -0
- package/dist/client.d.ts +34 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +93 -0
- package/dist/client.js.map +1 -0
- package/dist/config.d.ts +143 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +28 -0
- package/dist/config.js.map +1 -0
- package/dist/context.d.ts +44 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +26 -0
- package/dist/context.js.map +1 -0
- package/dist/dev-errors.d.ts +68 -0
- package/dist/dev-errors.d.ts.map +1 -0
- package/dist/dev-errors.js +121 -0
- package/dist/dev-errors.js.map +1 -0
- package/dist/devtools.d.ts +64 -0
- package/dist/devtools.d.ts.map +1 -0
- package/dist/devtools.js +186 -0
- package/dist/devtools.js.map +1 -0
- package/dist/frame-mirror.d.ts +19 -0
- package/dist/frame-mirror.d.ts.map +1 -0
- package/dist/frame-mirror.js +141 -0
- package/dist/frame-mirror.js.map +1 -0
- package/dist/frame-protocol.d.ts +175 -0
- package/dist/frame-protocol.d.ts.map +1 -0
- package/dist/frame-protocol.js +369 -0
- package/dist/frame-protocol.js.map +1 -0
- package/dist/history.d.ts +54 -0
- package/dist/history.d.ts.map +1 -0
- package/dist/history.js +152 -0
- package/dist/history.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/link.d.ts +32 -0
- package/dist/link.d.ts.map +1 -0
- package/dist/link.js +54 -0
- package/dist/link.js.map +1 -0
- package/dist/live-player.d.ts +83 -0
- package/dist/live-player.d.ts.map +1 -0
- package/dist/live-player.js +413 -0
- package/dist/live-player.js.map +1 -0
- package/dist/live-protocol.d.ts +132 -0
- package/dist/live-protocol.d.ts.map +1 -0
- package/dist/live-protocol.js +278 -0
- package/dist/live-protocol.js.map +1 -0
- package/dist/live-stub.d.ts +10 -0
- package/dist/live-stub.d.ts.map +1 -0
- package/dist/live-stub.js +32 -0
- package/dist/live-stub.js.map +1 -0
- package/dist/mcp/cli.d.ts +22 -0
- package/dist/mcp/cli.d.ts.map +1 -0
- package/dist/mcp/cli.js +177 -0
- package/dist/mcp/cli.js.map +1 -0
- package/dist/mcp/frame.d.ts +58 -0
- package/dist/mcp/frame.d.ts.map +1 -0
- package/dist/mcp/frame.js +169 -0
- package/dist/mcp/frame.js.map +1 -0
- package/dist/mcp/index.d.ts +15 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +15 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/inspect.d.ts +95 -0
- package/dist/mcp/inspect.d.ts.map +1 -0
- package/dist/mcp/inspect.js +386 -0
- package/dist/mcp/inspect.js.map +1 -0
- package/dist/mcp/keys.d.ts +20 -0
- package/dist/mcp/keys.d.ts.map +1 -0
- package/dist/mcp/keys.js +87 -0
- package/dist/mcp/keys.js.map +1 -0
- package/dist/mcp/logs.d.ts +63 -0
- package/dist/mcp/logs.d.ts.map +1 -0
- package/dist/mcp/logs.js +217 -0
- package/dist/mcp/logs.js.map +1 -0
- package/dist/mcp/server.d.ts +64 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +485 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/session.d.ts +230 -0
- package/dist/mcp/session.d.ts.map +1 -0
- package/dist/mcp/session.js +742 -0
- package/dist/mcp/session.js.map +1 -0
- package/dist/navigation-errors.d.ts +28 -0
- package/dist/navigation-errors.d.ts.map +1 -0
- package/dist/navigation-errors.js +43 -0
- package/dist/navigation-errors.js.map +1 -0
- package/dist/navigation.d.ts +21 -0
- package/dist/navigation.d.ts.map +1 -0
- package/dist/navigation.js +33 -0
- package/dist/navigation.js.map +1 -0
- package/dist/router.d.ts +76 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +296 -0
- package/dist/router.js.map +1 -0
- package/dist/routing.d.ts +101 -0
- package/dist/routing.d.ts.map +1 -0
- package/dist/routing.js +286 -0
- package/dist/routing.js.map +1 -0
- package/dist/server/adapters/build-step.d.ts +34 -0
- package/dist/server/adapters/build-step.d.ts.map +1 -0
- package/dist/server/adapters/build-step.js +112 -0
- package/dist/server/adapters/build-step.js.map +1 -0
- package/dist/server/adapters/bun.d.ts +8 -0
- package/dist/server/adapters/bun.d.ts.map +1 -0
- package/dist/server/adapters/bun.js +89 -0
- package/dist/server/adapters/bun.js.map +1 -0
- package/dist/server/adapters/bundle.d.ts +21 -0
- package/dist/server/adapters/bundle.d.ts.map +1 -0
- package/dist/server/adapters/bundle.js +86 -0
- package/dist/server/adapters/bundle.js.map +1 -0
- package/dist/server/adapters/cloudflare.d.ts +29 -0
- package/dist/server/adapters/cloudflare.d.ts.map +1 -0
- package/dist/server/adapters/cloudflare.js +105 -0
- package/dist/server/adapters/cloudflare.js.map +1 -0
- package/dist/server/adapters/index.d.ts +10 -0
- package/dist/server/adapters/index.d.ts.map +1 -0
- package/dist/server/adapters/index.js +24 -0
- package/dist/server/adapters/index.js.map +1 -0
- package/dist/server/adapters/runtime-node.d.ts +16 -0
- package/dist/server/adapters/runtime-node.d.ts.map +1 -0
- package/dist/server/adapters/runtime-node.js +29 -0
- package/dist/server/adapters/runtime-node.js.map +1 -0
- package/dist/server/adapters/runtime.d.ts +42 -0
- package/dist/server/adapters/runtime.d.ts.map +1 -0
- package/dist/server/adapters/runtime.js +115 -0
- package/dist/server/adapters/runtime.js.map +1 -0
- package/dist/server/adapters/shared.d.ts +66 -0
- package/dist/server/adapters/shared.d.ts.map +1 -0
- package/dist/server/adapters/shared.js +124 -0
- package/dist/server/adapters/shared.js.map +1 -0
- package/dist/server/adapters/static.d.ts +9 -0
- package/dist/server/adapters/static.d.ts.map +1 -0
- package/dist/server/adapters/static.js +70 -0
- package/dist/server/adapters/static.js.map +1 -0
- package/dist/server/adapters/vercel.d.ts +43 -0
- package/dist/server/adapters/vercel.d.ts.map +1 -0
- package/dist/server/adapters/vercel.js +119 -0
- package/dist/server/adapters/vercel.js.map +1 -0
- package/dist/server/bun-jsx.d.ts +18 -0
- package/dist/server/bun-jsx.d.ts.map +1 -0
- package/dist/server/bun-jsx.js +56 -0
- package/dist/server/bun-jsx.js.map +1 -0
- package/dist/server/cache-route.d.ts +38 -0
- package/dist/server/cache-route.d.ts.map +1 -0
- package/dist/server/cache-route.js +109 -0
- package/dist/server/cache-route.js.map +1 -0
- package/dist/server/cache.d.ts +131 -0
- package/dist/server/cache.d.ts.map +1 -0
- package/dist/server/cache.js +409 -0
- package/dist/server/cache.js.map +1 -0
- package/dist/server/frames.d.ts +45 -0
- package/dist/server/frames.d.ts.map +1 -0
- package/dist/server/frames.js +294 -0
- package/dist/server/frames.js.map +1 -0
- package/dist/server/http.d.ts +80 -0
- package/dist/server/http.d.ts.map +1 -0
- package/dist/server/http.js +453 -0
- package/dist/server/http.js.map +1 -0
- package/dist/server/jsx-dev-runtime.d.ts +5 -0
- package/dist/server/jsx-dev-runtime.d.ts.map +1 -0
- package/dist/server/jsx-dev-runtime.js +6 -0
- package/dist/server/jsx-dev-runtime.js.map +1 -0
- package/dist/server/jsx-runtime.d.ts +10 -0
- package/dist/server/jsx-runtime.d.ts.map +1 -0
- package/dist/server/jsx-runtime.js +11 -0
- package/dist/server/jsx-runtime.js.map +1 -0
- package/dist/server/live-app.d.ts +87 -0
- package/dist/server/live-app.d.ts.map +1 -0
- package/dist/server/live-app.js +307 -0
- package/dist/server/live-app.js.map +1 -0
- package/dist/server/live-build.d.ts +70 -0
- package/dist/server/live-build.d.ts.map +1 -0
- package/dist/server/live-build.js +222 -0
- package/dist/server/live-build.js.map +1 -0
- package/dist/server/live-dev.d.ts +62 -0
- package/dist/server/live-dev.d.ts.map +1 -0
- package/dist/server/live-dev.js +313 -0
- package/dist/server/live-dev.js.map +1 -0
- package/dist/server/live-manifest.d.ts +23 -0
- package/dist/server/live-manifest.d.ts.map +1 -0
- package/dist/server/live-manifest.js +7 -0
- package/dist/server/live-manifest.js.map +1 -0
- package/dist/server/live-prod.d.ts +30 -0
- package/dist/server/live-prod.d.ts.map +1 -0
- package/dist/server/live-prod.js +97 -0
- package/dist/server/live-prod.js.map +1 -0
- package/dist/server/live-routes.d.ts +72 -0
- package/dist/server/live-routes.d.ts.map +1 -0
- package/dist/server/live-routes.js +274 -0
- package/dist/server/live-routes.js.map +1 -0
- package/dist/server/live-web.d.ts +16 -0
- package/dist/server/live-web.d.ts.map +1 -0
- package/dist/server/live-web.js +159 -0
- package/dist/server/live-web.js.map +1 -0
- package/dist/server/live.d.ts +138 -0
- package/dist/server/live.d.ts.map +1 -0
- package/dist/server/live.js +542 -0
- package/dist/server/live.js.map +1 -0
- package/dist/server/middleware-node.d.ts +48 -0
- package/dist/server/middleware-node.d.ts.map +1 -0
- package/dist/server/middleware-node.js +167 -0
- package/dist/server/middleware-node.js.map +1 -0
- package/dist/server/middleware-runtime.d.ts +97 -0
- package/dist/server/middleware-runtime.d.ts.map +1 -0
- package/dist/server/middleware-runtime.js +447 -0
- package/dist/server/middleware-runtime.js.map +1 -0
- package/dist/server/middleware.d.ts +145 -0
- package/dist/server/middleware.d.ts.map +1 -0
- package/dist/server/middleware.js +365 -0
- package/dist/server/middleware.js.map +1 -0
- package/dist/server/ppr.d.ts +22 -0
- package/dist/server/ppr.d.ts.map +1 -0
- package/dist/server/ppr.js +58 -0
- package/dist/server/ppr.js.map +1 -0
- package/dist/server/scan.d.ts +48 -0
- package/dist/server/scan.d.ts.map +1 -0
- package/dist/server/scan.js +161 -0
- package/dist/server/scan.js.map +1 -0
- package/dist/server/server-components.d.ts +65 -0
- package/dist/server/server-components.d.ts.map +1 -0
- package/dist/server/server-components.js +215 -0
- package/dist/server/server-components.js.map +1 -0
- package/dist/terminal.d.ts +5 -0
- package/dist/terminal.d.ts.map +1 -0
- package/dist/terminal.js +5 -0
- package/dist/terminal.js.map +1 -0
- package/dist/testing.d.ts +5 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +5 -0
- package/dist/testing.js.map +1 -0
- package/dist/vite-plugin.d.ts +51 -0
- package/dist/vite-plugin.d.ts.map +1 -0
- package/dist/vite-plugin.js +403 -0
- package/dist/vite-plugin.js.map +1 -0
- package/package.json +130 -4
- package/src/cli/build.ts +182 -0
- package/src/cli/config.ts +72 -0
- package/src/cli/dev.ts +54 -0
- package/src/cli/index.ts +185 -0
- package/src/cli/run.ts +124 -0
- package/src/cli/start.ts +194 -0
- package/src/cli/ui.ts +93 -0
- package/src/client.tsx +122 -0
- package/src/config.ts +155 -0
- package/src/context.ts +69 -0
- package/src/dev-errors.ts +167 -0
- package/src/devtools.tsx +419 -0
- package/src/frame-mirror.ts +145 -0
- package/src/frame-protocol.ts +435 -0
- package/src/history.ts +201 -0
- package/src/index.ts +9 -0
- package/src/link.tsx +108 -0
- package/src/live-player.ts +449 -0
- package/src/live-protocol.ts +317 -0
- package/src/live-stub.tsx +32 -0
- package/src/mcp/cli.ts +188 -0
- package/src/mcp/frame.ts +224 -0
- package/src/mcp/index.ts +15 -0
- package/src/mcp/inspect.ts +445 -0
- package/src/mcp/keys.ts +86 -0
- package/src/mcp/logs.ts +225 -0
- package/src/mcp/server.ts +595 -0
- package/src/mcp/session.ts +821 -0
- package/src/navigation-errors.ts +60 -0
- package/src/navigation.ts +44 -0
- package/src/router.tsx +497 -0
- package/src/routing.ts +344 -0
- package/src/server/adapters/build-step.ts +135 -0
- package/src/server/adapters/bun.ts +92 -0
- package/src/server/adapters/bundle.ts +97 -0
- package/src/server/adapters/cloudflare.ts +111 -0
- package/src/server/adapters/index.ts +29 -0
- package/src/server/adapters/runtime-node.ts +34 -0
- package/src/server/adapters/runtime.ts +145 -0
- package/src/server/adapters/shared.ts +159 -0
- package/src/server/adapters/static.ts +68 -0
- package/src/server/adapters/vercel.ts +149 -0
- package/src/server/bun-jsx.ts +65 -0
- package/src/server/cache-route.ts +139 -0
- package/src/server/cache.ts +498 -0
- package/src/server/frames.ts +326 -0
- package/src/server/http.ts +467 -0
- package/src/server/jsx-dev-runtime.ts +9 -0
- package/src/server/jsx-runtime.ts +13 -0
- package/src/server/live-app.tsx +374 -0
- package/src/server/live-build.ts +258 -0
- package/src/server/live-dev.ts +357 -0
- package/src/server/live-manifest.ts +25 -0
- package/src/server/live-prod.ts +120 -0
- package/src/server/live-routes.ts +298 -0
- package/src/server/live-web.ts +170 -0
- package/src/server/live.ts +551 -0
- package/src/server/middleware-node.ts +168 -0
- package/src/server/middleware-runtime.ts +478 -0
- package/src/server/middleware.ts +451 -0
- package/src/server/ppr.ts +62 -0
- package/src/server/scan.ts +190 -0
- package/src/server/server-components.ts +224 -0
- package/src/terminal.ts +5 -0
- package/src/testing.ts +5 -0
- package/src/vite-plugin.ts +413 -0
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `text dev` wiring for server-rendered console routes and the SSR graph they
|
|
3
|
+
* render in: the `@textjs/app:live` Vite plugin (virtual live / player
|
|
4
|
+
* entries, client stubs for server pages, the async-component JSX runtime on
|
|
5
|
+
* the server) and the dev request handler (live sessions + player HTML).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
9
|
+
import { join, relative, sep } from "node:path";
|
|
10
|
+
import type { Plugin, ViteDevServer } from "vite";
|
|
11
|
+
import type { ViewportConfig } from "../config";
|
|
12
|
+
import { LIVE_PATH } from "../live-protocol";
|
|
13
|
+
import type { RouteManifest } from "../routing";
|
|
14
|
+
import type { ThemeName } from "@textjs/react";
|
|
15
|
+
import { LiveServer } from "./live";
|
|
16
|
+
import type { LiveApp, LiveAppOptions, RouteRuntime } from "./live-app";
|
|
17
|
+
import {
|
|
18
|
+
createLiveHtml,
|
|
19
|
+
createRuntimeMatcher,
|
|
20
|
+
generateLiveEntryModule,
|
|
21
|
+
generatePlayerEntryModule,
|
|
22
|
+
JSX_DEV_RUNTIME_MODULE,
|
|
23
|
+
JSX_RUNTIME_MODULE,
|
|
24
|
+
LIVE_STUB_MODULE,
|
|
25
|
+
listServerPages,
|
|
26
|
+
PLAYER_URL,
|
|
27
|
+
SERVER_ONLY_QUERY,
|
|
28
|
+
SERVER_STUB_QUERY,
|
|
29
|
+
VIRTUAL_LIVE,
|
|
30
|
+
VIRTUAL_PLAYER,
|
|
31
|
+
} from "./live-routes";
|
|
32
|
+
import { scanApp } from "./scan";
|
|
33
|
+
import type { MiddlewareModule } from "./middleware";
|
|
34
|
+
import { findMiddlewareFile } from "./middleware-node";
|
|
35
|
+
import { createLiveAuthorizer } from "./middleware-runtime";
|
|
36
|
+
|
|
37
|
+
const RESOLVED_LIVE = `\0${VIRTUAL_LIVE}`;
|
|
38
|
+
const RESOLVED_PLAYER = `\0${VIRTUAL_PLAYER}`;
|
|
39
|
+
const STUB_PREFIX = "\0textjs-server-stub:";
|
|
40
|
+
const SERVER_ONLY_PREFIX = "\0textjs-server-only:";
|
|
41
|
+
/** `*.server.ts`, `*.server.tsx`, `foo.server` (any extension or none). */
|
|
42
|
+
const SERVER_MODULE = /\.server(?:\.[cm]?[jt]sx?)?$/;
|
|
43
|
+
|
|
44
|
+
export interface LiveEntryModule {
|
|
45
|
+
routes: RouteManifest;
|
|
46
|
+
createLiveApp(options: LiveAppOptions): Promise<LiveApp>;
|
|
47
|
+
prerenderShell(options: import("./live-app").PrerenderShellOptions): Promise<import("./live-app").ShellCall[]>;
|
|
48
|
+
routeRuntime(routes: RouteManifest, pathname: string): RouteRuntime;
|
|
49
|
+
isServerPage(routes: RouteManifest, pathname: string): boolean;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** `react/jsx-runtime` / `react/jsx-dev-runtime` as an import or re-export specifier (not any string in the code). */
|
|
53
|
+
const JSX_IMPORT = /(\bfrom\s*|\bimport\s*\(?\s*)(["'])react\/(jsx-runtime|jsx-dev-runtime)\2/g;
|
|
54
|
+
|
|
55
|
+
const VLQ = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
56
|
+
|
|
57
|
+
function vlq(value: number): string {
|
|
58
|
+
let v = value < 0 ? (-value << 1) | 1 : value << 1;
|
|
59
|
+
let out = "";
|
|
60
|
+
do {
|
|
61
|
+
let digit = v & 31;
|
|
62
|
+
v >>>= 5;
|
|
63
|
+
if (v > 0) digit |= 32;
|
|
64
|
+
out += VLQ[digit];
|
|
65
|
+
} while (v > 0);
|
|
66
|
+
return out;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Apply same-line replacements and return a source map back to the original:
|
|
71
|
+
* every line maps column 0 to itself, and code after each replacement maps to
|
|
72
|
+
* where it was before (so stack traces and breakpoints stay exact).
|
|
73
|
+
*/
|
|
74
|
+
export function replaceWithMap(code: string, re: RegExp, replace: (match: RegExpExecArray) => string | null, source: string): { code: string; map: { version: 3; sources: string[]; names: string[]; mappings: string } } | null {
|
|
75
|
+
const lines = code.split("\n");
|
|
76
|
+
let changed = false;
|
|
77
|
+
let prevLine = 0;
|
|
78
|
+
let prevCol = 0;
|
|
79
|
+
const mappings: string[] = [];
|
|
80
|
+
const out = lines.map((line, y) => {
|
|
81
|
+
const segments: Array<[number, number]> = [[0, 0]];
|
|
82
|
+
let result = "";
|
|
83
|
+
let last = 0;
|
|
84
|
+
re.lastIndex = 0;
|
|
85
|
+
for (let m = re.exec(line); m !== null; m = re.exec(line)) {
|
|
86
|
+
const next = replace(m);
|
|
87
|
+
if (next === null) continue;
|
|
88
|
+
changed = true;
|
|
89
|
+
result += line.slice(last, m.index) + next;
|
|
90
|
+
last = m.index + m[0].length;
|
|
91
|
+
segments.push([result.length, last]);
|
|
92
|
+
}
|
|
93
|
+
result += line.slice(last);
|
|
94
|
+
let genCol = 0;
|
|
95
|
+
mappings.push(
|
|
96
|
+
segments
|
|
97
|
+
.map(([gen, orig]) => {
|
|
98
|
+
const seg = vlq(gen - genCol) + vlq(0) + vlq(y - prevLine) + vlq(orig - prevCol);
|
|
99
|
+
genCol = gen;
|
|
100
|
+
prevLine = y;
|
|
101
|
+
prevCol = orig;
|
|
102
|
+
return seg;
|
|
103
|
+
})
|
|
104
|
+
.join(","),
|
|
105
|
+
);
|
|
106
|
+
return result;
|
|
107
|
+
});
|
|
108
|
+
return changed ? { code: out.join("\n"), map: { version: 3, sources: [source], names: [], mappings: mappings.join(";") } } : null;
|
|
109
|
+
}
|
|
110
|
+
const SHIM_FILE = /[\\/]server[\\/]jsx-(dev-)?runtime\.[cm]?[jt]s$/;
|
|
111
|
+
|
|
112
|
+
function toPosixPath(file: string, root: string): string {
|
|
113
|
+
const rel = relative(root, file);
|
|
114
|
+
return (rel.startsWith("..") ? file : rel).split(sep).join("/");
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Vite plugins (dev, client build and SSR builds). */
|
|
118
|
+
export function textjsLivePlugin(options: { appDir?: string; root?: string } = {}): Plugin[] {
|
|
119
|
+
const appDir = options.appDir ?? "app";
|
|
120
|
+
let root = options.root ?? process.cwd();
|
|
121
|
+
|
|
122
|
+
const live: Plugin = {
|
|
123
|
+
name: "@textjs/app:live",
|
|
124
|
+
enforce: "pre",
|
|
125
|
+
config(user) {
|
|
126
|
+
if (user.root) root = user.root;
|
|
127
|
+
// One instance of the framework (and its React contexts) in the SSR graph,
|
|
128
|
+
// whether @textjs/* is linked from a workspace or installed.
|
|
129
|
+
return { ssr: { noExternal: [/^@textjs\//] } };
|
|
130
|
+
},
|
|
131
|
+
configResolved(config) {
|
|
132
|
+
root = config.root;
|
|
133
|
+
},
|
|
134
|
+
resolveId(id, importer, opts) {
|
|
135
|
+
if (id === VIRTUAL_LIVE || id.endsWith(`/${VIRTUAL_LIVE}`)) return RESOLVED_LIVE;
|
|
136
|
+
if (id === VIRTUAL_PLAYER || id === PLAYER_URL || id.endsWith(PLAYER_URL)) return RESOLVED_PLAYER;
|
|
137
|
+
if (id.endsWith(SERVER_STUB_QUERY)) return `${STUB_PREFIX}${id.slice(0, -SERVER_STUB_QUERY.length)}`;
|
|
138
|
+
if (id.endsWith(SERVER_ONLY_QUERY)) return `${SERVER_ONLY_PREFIX}${id.slice(0, -SERVER_ONLY_QUERY.length)}`;
|
|
139
|
+
// Client graph guard: browser code (yours, not dependencies') must not import server-only modules.
|
|
140
|
+
// The dependency scan crawls every file under `app/`, server pages and route handlers included: it isn't the client graph.
|
|
141
|
+
if (!opts?.ssr && !(opts as { scan?: boolean } | undefined)?.scan && importer && !importer.startsWith("\0") && !importer.includes(`${sep}node_modules${sep}`) && !importer.includes("/node_modules/")) {
|
|
142
|
+
const bare = id.split("?")[0] as string;
|
|
143
|
+
if (bare.startsWith("node:") || SERVER_MODULE.test(bare)) {
|
|
144
|
+
this.error(
|
|
145
|
+
`▼ Text.js: ${toPosixPath(importer.split("?")[0] as string, root)} imports ${JSON.stringify(id)}, which only runs on the server, from code that ships to the browser. ` +
|
|
146
|
+
`Import it from a page with \`export const runtime = "server"\` (or its server components), or from a route handler.`,
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return null;
|
|
151
|
+
},
|
|
152
|
+
load(id) {
|
|
153
|
+
if (id === RESOLVED_LIVE) return generateLiveEntryModule(scanApp(root, appDir));
|
|
154
|
+
if (id === RESOLVED_PLAYER) return generatePlayerEntryModule();
|
|
155
|
+
if (id.startsWith(STUB_PREFIX)) return `export { default, runtime } from ${JSON.stringify(LIVE_STUB_MODULE)};\n`;
|
|
156
|
+
// Layouts / boundaries of server-only segments: the browser never renders them.
|
|
157
|
+
if (id.startsWith(SERVER_ONLY_PREFIX)) return "export default function ServerOnly(props) { return props.children ?? null; }\n";
|
|
158
|
+
return null;
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* On the server, compiled JSX imports the async-component-aware runtime
|
|
164
|
+
* (`@textjs/app/server/jsx-runtime`) instead of React's. Runs after
|
|
165
|
+
* esbuild has emitted the `react/jsx-runtime` import.
|
|
166
|
+
*/
|
|
167
|
+
const shims = new Map<string, Promise<string | null>>();
|
|
168
|
+
const serverJsx: Plugin = {
|
|
169
|
+
name: "@textjs/app:server-jsx",
|
|
170
|
+
enforce: "post",
|
|
171
|
+
async transform(code, id, opts) {
|
|
172
|
+
if (!opts?.ssr || id.startsWith("\0") || SHIM_FILE.test(id.split("?")[0] as string)) return null;
|
|
173
|
+
if (!code.includes("react/jsx-")) return null;
|
|
174
|
+
JSX_IMPORT.lastIndex = 0;
|
|
175
|
+
if (!JSX_IMPORT.test(code)) return null;
|
|
176
|
+
// Resolved from the project root (as user code would), then imported by absolute id,
|
|
177
|
+
// so framework packages that don't depend on @textjs/app can use it too.
|
|
178
|
+
const resolveShim = (spec: string): Promise<string | null> => {
|
|
179
|
+
let p = shims.get(spec);
|
|
180
|
+
if (p === undefined) {
|
|
181
|
+
p = this.resolve(spec, join(root, "package.json"), { skipSelf: true }).then((r) => r?.id ?? null);
|
|
182
|
+
shims.set(spec, p);
|
|
183
|
+
}
|
|
184
|
+
return p;
|
|
185
|
+
};
|
|
186
|
+
const runtime = await resolveShim(JSX_RUNTIME_MODULE);
|
|
187
|
+
const dev = await resolveShim(JSX_DEV_RUNTIME_MODULE);
|
|
188
|
+
return replaceWithMap(
|
|
189
|
+
code,
|
|
190
|
+
JSX_IMPORT,
|
|
191
|
+
(m) => {
|
|
192
|
+
const target = m[3] === "jsx-runtime" ? runtime : dev;
|
|
193
|
+
return target ? `${m[1]}${JSON.stringify(target)}` : null;
|
|
194
|
+
},
|
|
195
|
+
id.split("?")[0] as string,
|
|
196
|
+
);
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
return [live, serverJsx];
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export interface LiveDevOptions {
|
|
204
|
+
root: string;
|
|
205
|
+
appDir?: string;
|
|
206
|
+
basePath: string;
|
|
207
|
+
title: string;
|
|
208
|
+
viewport?: ViewportConfig;
|
|
209
|
+
live?: { maxSessions?: number; reconnectGraceMs?: number };
|
|
210
|
+
logger?: { info(message: string): void; error(message: string): void };
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export interface LiveDevHandler {
|
|
214
|
+
readonly server: LiveServer;
|
|
215
|
+
/** `url` has `basePath` stripped. Returns `true` when the request was answered. */
|
|
216
|
+
handle(req: IncomingMessage, res: ServerResponse, url: string): Promise<boolean>;
|
|
217
|
+
close(): void;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function wantsHtml(req: IncomingMessage, pathname: string): boolean {
|
|
221
|
+
if (req.method !== "GET" && req.method !== "HEAD") return false;
|
|
222
|
+
if (pathname.startsWith("/@") || pathname.startsWith("/__") || pathname.startsWith("/node_modules/")) return false;
|
|
223
|
+
const last = pathname.split("/").pop() ?? "";
|
|
224
|
+
if (last.includes(".")) return false;
|
|
225
|
+
const accept = req.headers.accept;
|
|
226
|
+
return accept === undefined || accept.includes("text/html") || accept.includes("*/*");
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/** Live sessions and player HTML for `text dev`. */
|
|
230
|
+
export function createLiveDevHandler(vite: ViteDevServer, o: LiveDevOptions): LiveDevHandler {
|
|
231
|
+
const appDirName = o.appDir ?? "app";
|
|
232
|
+
const appDir = join(o.root, appDirName);
|
|
233
|
+
let scan = scanApp(o.root, appDirName);
|
|
234
|
+
let runtimeOf = createRuntimeMatcher(scan);
|
|
235
|
+
let serverSig = JSON.stringify(listServerPages(scan));
|
|
236
|
+
const log = o.logger;
|
|
237
|
+
|
|
238
|
+
const loadEntry = async (): Promise<LiveEntryModule> => (await vite.ssrLoadModule(VIRTUAL_LIVE)) as LiveEntryModule;
|
|
239
|
+
|
|
240
|
+
const live = new LiveServer({
|
|
241
|
+
basePath: o.basePath,
|
|
242
|
+
exposeErrors: true,
|
|
243
|
+
// middleware.ts guards server routes a session renders (opening, in-app navigation, credential changes).
|
|
244
|
+
authorize: createLiveAuthorizer(
|
|
245
|
+
async () => {
|
|
246
|
+
const file = findMiddlewareFile(o.root);
|
|
247
|
+
return file ? ((await vite.ssrLoadModule(file)) as MiddlewareModule) : null;
|
|
248
|
+
},
|
|
249
|
+
{ basePath: o.basePath },
|
|
250
|
+
),
|
|
251
|
+
maxSessions: o.live?.maxSessions,
|
|
252
|
+
reconnectGraceMs: o.live?.reconnectGraceMs,
|
|
253
|
+
runtimeOf: (p) => runtimeOf(p),
|
|
254
|
+
log: (m) => log?.info(m),
|
|
255
|
+
onError: (error) => {
|
|
256
|
+
if (error instanceof Error) vite.ssrFixStacktrace(error);
|
|
257
|
+
log?.error(` ⨯ ${error instanceof Error ? (error.stack ?? error.message) : String(error)}`);
|
|
258
|
+
},
|
|
259
|
+
createApp: async (init) => {
|
|
260
|
+
const mod = await loadEntry();
|
|
261
|
+
return mod.createLiveApp({ ...init, routes: mod.routes, theme: o.viewport?.theme as ThemeName | undefined, defaultTitle: o.title || undefined });
|
|
262
|
+
},
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Re-execute the changed files (and what imports them) in the SSR graph and
|
|
267
|
+
* re-render every session. A route structure change also regenerates the
|
|
268
|
+
* virtual live module.
|
|
269
|
+
*/
|
|
270
|
+
const changed = new Set<string>();
|
|
271
|
+
let structureChanged = false;
|
|
272
|
+
const reloadSessions = async (): Promise<void> => {
|
|
273
|
+
const files = [...changed];
|
|
274
|
+
changed.clear();
|
|
275
|
+
const structure = structureChanged;
|
|
276
|
+
structureChanged = false;
|
|
277
|
+
if (live.sessions.size === 0) return;
|
|
278
|
+
const graph = vite.environments?.ssr?.moduleGraph ?? vite.moduleGraph;
|
|
279
|
+
type GraphModule = { id: string | null; importers: Set<GraphModule> };
|
|
280
|
+
const seen = new Set<GraphModule>();
|
|
281
|
+
const invalidate = (mod: GraphModule): void => {
|
|
282
|
+
if (seen.has(mod)) return;
|
|
283
|
+
seen.add(mod);
|
|
284
|
+
graph.invalidateModule(mod as never);
|
|
285
|
+
for (const importer of mod.importers) invalidate(importer);
|
|
286
|
+
};
|
|
287
|
+
for (const file of files) for (const mod of graph.getModulesByFile(file) ?? []) invalidate(mod as unknown as GraphModule);
|
|
288
|
+
if (structure) {
|
|
289
|
+
const virtual = graph.getModuleById(`\0${VIRTUAL_LIVE}`);
|
|
290
|
+
if (virtual) invalidate(virtual as unknown as GraphModule);
|
|
291
|
+
}
|
|
292
|
+
try {
|
|
293
|
+
const mod = await loadEntry();
|
|
294
|
+
live.forEachApp((app) => app.setRoutes(mod.routes));
|
|
295
|
+
log?.info(` ✓ Live sessions refreshed (${live.sessions.size})`);
|
|
296
|
+
} catch (error) {
|
|
297
|
+
log?.error(` ⨯ ${error instanceof Error ? (error.stack ?? error.message) : String(error)}`);
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
302
|
+
const rootPrefix = o.root.endsWith(sep) ? o.root : o.root + sep;
|
|
303
|
+
const appPrefix = appDir.endsWith(sep) ? appDir : appDir + sep;
|
|
304
|
+
const onChange = (file: string, kind: "change" | "add" | "unlink"): void => {
|
|
305
|
+
if (!file.startsWith(rootPrefix) || file.includes(`${sep}node_modules${sep}`)) return;
|
|
306
|
+
changed.add(file);
|
|
307
|
+
if (kind !== "change" && file.startsWith(appPrefix)) structureChanged = true;
|
|
308
|
+
if (timer !== null) clearTimeout(timer);
|
|
309
|
+
timer = setTimeout(() => {
|
|
310
|
+
timer = null;
|
|
311
|
+
if (file.startsWith(appPrefix)) {
|
|
312
|
+
scan = scanApp(o.root, appDirName);
|
|
313
|
+
runtimeOf = createRuntimeMatcher(scan);
|
|
314
|
+
const sig = JSON.stringify(listServerPages(scan));
|
|
315
|
+
if (sig !== serverSig) {
|
|
316
|
+
// A page switched runtimes: the client manifest swaps its stub.
|
|
317
|
+
serverSig = sig;
|
|
318
|
+
const routesMod = vite.moduleGraph.getModuleById("\0virtual:textjs/routes");
|
|
319
|
+
if (routesMod) vite.moduleGraph.invalidateModule(routesMod);
|
|
320
|
+
vite.ws.send({ type: "full-reload" });
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
void reloadSessions();
|
|
324
|
+
}, 50);
|
|
325
|
+
};
|
|
326
|
+
const onFileChange = (file: string): void => onChange(file, "change");
|
|
327
|
+
const onAdd = (file: string): void => onChange(file, "add");
|
|
328
|
+
const onUnlink = (file: string): void => onChange(file, "unlink");
|
|
329
|
+
vite.watcher.on("change", onFileChange);
|
|
330
|
+
vite.watcher.on("add", onAdd);
|
|
331
|
+
vite.watcher.on("unlink", onUnlink);
|
|
332
|
+
vite.httpServer?.once("close", () => live.close());
|
|
333
|
+
|
|
334
|
+
return {
|
|
335
|
+
server: live,
|
|
336
|
+
async handle(req, res, url) {
|
|
337
|
+
const pathname = url.split("?")[0] ?? "/";
|
|
338
|
+
if (pathname === LIVE_PATH) return live.handle(req, res, url);
|
|
339
|
+
if (!wantsHtml(req, pathname) || runtimeOf(pathname) !== "server") return false;
|
|
340
|
+
const raw = createLiveHtml({ title: o.title, basePath: o.basePath, viewport: o.viewport, playerSrc: `${o.basePath}${PLAYER_URL}` });
|
|
341
|
+
// As in the page handler (`HTML_TRANSFORM_URL`): never the request URL, whose query string makes Vite watch `/`.
|
|
342
|
+
const html = await vite.transformIndexHtml("/index.html", raw, (req as { originalUrl?: string }).originalUrl);
|
|
343
|
+
res.statusCode = 200;
|
|
344
|
+
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
345
|
+
res.setHeader("Cache-Control", "no-cache");
|
|
346
|
+
res.end(req.method === "HEAD" ? undefined : html);
|
|
347
|
+
return true;
|
|
348
|
+
},
|
|
349
|
+
close() {
|
|
350
|
+
if (timer !== null) clearTimeout(timer);
|
|
351
|
+
vite.watcher.off("change", onFileChange);
|
|
352
|
+
vite.watcher.off("add", onAdd);
|
|
353
|
+
vite.watcher.off("unlink", onUnlink);
|
|
354
|
+
live.close();
|
|
355
|
+
},
|
|
356
|
+
};
|
|
357
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `.text/live-manifest.json`: what `text start` and the Bun server need to
|
|
3
|
+
* serve prerendered shells and server-rendered routes (kept free of build-time
|
|
4
|
+
* imports so server bundles can read it).
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { TextConfig } from "../config";
|
|
8
|
+
|
|
9
|
+
export const LIVE_MANIFEST = "live-manifest.json";
|
|
10
|
+
|
|
11
|
+
export interface LiveManifest {
|
|
12
|
+
version: 1;
|
|
13
|
+
basePath: string;
|
|
14
|
+
title: string;
|
|
15
|
+
viewport: TextConfig["viewport"];
|
|
16
|
+
/** Player script URL (with `basePath`), when any route renders on the server. */
|
|
17
|
+
player: string | null;
|
|
18
|
+
/** Patterns of server-rendered pages. */
|
|
19
|
+
serverRoutes: string[];
|
|
20
|
+
/** Static HTML per path (relative to the output directory), shells included. */
|
|
21
|
+
pages: Record<string, string>;
|
|
22
|
+
/** Player document for dynamic server routes (relative to the output directory). */
|
|
23
|
+
liveHtml: string | null;
|
|
24
|
+
live?: TextConfig["live"];
|
|
25
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `text start` half of server-rendered routes and Partial Prerendering: serves
|
|
3
|
+
* prerendered shells, the frame player document for server routes, and the
|
|
4
|
+
* live session endpoints, all from what `text build` wrote to `.text/`.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
8
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import { pathToFileURL } from "node:url";
|
|
11
|
+
import type { ThemeName } from "@textjs/react";
|
|
12
|
+
import { LIVE_PATH } from "../live-protocol";
|
|
13
|
+
import { LiveServer, type LiveServerOptions } from "./live";
|
|
14
|
+
import type { MiddlewareModule } from "./middleware";
|
|
15
|
+
import { createLiveAuthorizer } from "./middleware-runtime";
|
|
16
|
+
import { LIVE_MANIFEST, type LiveManifest } from "./live-manifest";
|
|
17
|
+
import type { LiveEntryModule } from "./live-dev";
|
|
18
|
+
|
|
19
|
+
export interface LiveProductionOptions {
|
|
20
|
+
log?(message: string): void;
|
|
21
|
+
onError?(error: unknown): void;
|
|
22
|
+
/** `middleware.ts`: authorizes every server route a session renders (unless `authorize` is given). */
|
|
23
|
+
middleware?: MiddlewareModule;
|
|
24
|
+
authorize?: LiveServerOptions["authorize"];
|
|
25
|
+
/** Behind a reverse proxy: trust `X-Forwarded-*` (also for the origin middleware sees). */
|
|
26
|
+
trustProxy?: boolean;
|
|
27
|
+
/** Fixed public origin for middleware's `request.url`. */
|
|
28
|
+
origin?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface LiveProduction {
|
|
32
|
+
readonly manifest: LiveManifest;
|
|
33
|
+
readonly server: LiveServer | null;
|
|
34
|
+
/** `url` has `basePath` stripped. Returns `true` when the request was answered. */
|
|
35
|
+
handle(req: IncomingMessage, res: ServerResponse, url: string): Promise<boolean>;
|
|
36
|
+
close(): void;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function wantsHtml(req: IncomingMessage, pathname: string): boolean {
|
|
40
|
+
if (req.method !== "GET" && req.method !== "HEAD") return false;
|
|
41
|
+
const last = pathname.split("/").pop() ?? "";
|
|
42
|
+
if (last.includes(".")) return false;
|
|
43
|
+
const accept = req.headers.accept;
|
|
44
|
+
return accept === undefined || accept.includes("text/html") || accept.includes("*/*");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function normalize(pathname: string): string {
|
|
48
|
+
let p = pathname;
|
|
49
|
+
try {
|
|
50
|
+
p = decodeURIComponent(p);
|
|
51
|
+
} catch {
|
|
52
|
+
// keep raw
|
|
53
|
+
}
|
|
54
|
+
return p.length > 1 && p.endsWith("/") ? p.slice(0, -1) : p;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Load `.text/live-manifest.json` (and the live bundle when routes render on the server). */
|
|
58
|
+
export async function loadLiveProduction(outDir: string, options: LiveProductionOptions = {}): Promise<LiveProduction | null> {
|
|
59
|
+
const file = join(outDir, ".text", LIVE_MANIFEST);
|
|
60
|
+
if (!existsSync(file)) return null;
|
|
61
|
+
const manifest = JSON.parse(readFileSync(file, "utf8")) as LiveManifest;
|
|
62
|
+
const html = new Map<string, Buffer>();
|
|
63
|
+
for (const [pattern, rel] of Object.entries(manifest.pages)) {
|
|
64
|
+
const p = join(outDir, rel);
|
|
65
|
+
if (existsSync(p)) html.set(pattern, readFileSync(p));
|
|
66
|
+
}
|
|
67
|
+
const liveHtml = manifest.liveHtml && existsSync(join(outDir, manifest.liveHtml)) ? readFileSync(join(outDir, manifest.liveHtml)) : null;
|
|
68
|
+
|
|
69
|
+
let server: LiveServer | null = null;
|
|
70
|
+
let runtimeOf: (pathname: string) => "server" | "client" = () => "client";
|
|
71
|
+
if (manifest.serverRoutes.length > 0) {
|
|
72
|
+
const entry = (await import(pathToFileURL(join(outDir, ".text", "server", "live.mjs")).href)) as LiveEntryModule;
|
|
73
|
+
runtimeOf = (pathname) => (entry.isServerPage(entry.routes, pathname) ? "server" : "client");
|
|
74
|
+
server = new LiveServer({
|
|
75
|
+
basePath: manifest.basePath,
|
|
76
|
+
maxSessions: manifest.live?.maxSessions,
|
|
77
|
+
reconnectGraceMs: manifest.live?.reconnectGraceMs,
|
|
78
|
+
runtimeOf,
|
|
79
|
+
log: options.log,
|
|
80
|
+
onError: options.onError,
|
|
81
|
+
trustProxy: options.trustProxy,
|
|
82
|
+
authorize:
|
|
83
|
+
options.authorize ??
|
|
84
|
+
(options.middleware ? createLiveAuthorizer(() => options.middleware, { basePath: manifest.basePath, trustProxy: options.trustProxy, origin: options.origin }) : undefined),
|
|
85
|
+
createApp: (init) =>
|
|
86
|
+
entry.createLiveApp({ ...init, routes: entry.routes, theme: manifest.viewport?.theme as ThemeName | undefined, defaultTitle: manifest.title || undefined }),
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const send = (req: IncomingMessage, res: ServerResponse, body: Buffer): true => {
|
|
91
|
+
res.statusCode = 200;
|
|
92
|
+
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
93
|
+
res.setHeader("Cache-Control", "public, max-age=0, must-revalidate");
|
|
94
|
+
res.end(req.method === "HEAD" ? undefined : body);
|
|
95
|
+
return true;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
manifest,
|
|
100
|
+
server,
|
|
101
|
+
async handle(req, res, url) {
|
|
102
|
+
const pathname = url.split("?")[0] ?? "/";
|
|
103
|
+
if (pathname === LIVE_PATH) {
|
|
104
|
+
if (server) return server.handle(req, res, url);
|
|
105
|
+
res.statusCode = 404;
|
|
106
|
+
res.end();
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
if (!wantsHtml(req, pathname)) return false;
|
|
110
|
+
const path = normalize(pathname);
|
|
111
|
+
const page = html.get(path);
|
|
112
|
+
if (page) return send(req, res, page);
|
|
113
|
+
if (server && liveHtml && runtimeOf(path) === "server") return send(req, res, liveHtml);
|
|
114
|
+
return false;
|
|
115
|
+
},
|
|
116
|
+
close() {
|
|
117
|
+
server?.close();
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
}
|