@rangojs/router 0.0.0-experimental.8123bb7e → 0.0.0-experimental.82
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 +76 -18
- package/dist/bin/rango.js +130 -47
- package/dist/vite/index.js +829 -380
- package/dist/vite/index.js.bak +5448 -0
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +4 -4
- package/skills/handler-use/SKILL.md +362 -0
- package/skills/hooks/SKILL.md +24 -18
- package/skills/intercept/SKILL.md +20 -0
- package/skills/layout/SKILL.md +22 -0
- package/skills/links/SKILL.md +3 -1
- package/skills/middleware/SKILL.md +34 -3
- package/skills/migrate-nextjs/SKILL.md +560 -0
- package/skills/migrate-react-router/SKILL.md +765 -0
- package/skills/parallel/SKILL.md +59 -0
- package/skills/prerender/SKILL.md +110 -68
- package/skills/rango/SKILL.md +24 -22
- package/skills/route/SKILL.md +24 -0
- package/skills/router-setup/SKILL.md +35 -0
- package/src/__internal.ts +1 -1
- package/src/browser/app-version.ts +14 -0
- package/src/browser/navigation-bridge.ts +37 -5
- package/src/browser/navigation-client.ts +128 -77
- package/src/browser/navigation-store.ts +43 -8
- package/src/browser/partial-update.ts +41 -7
- package/src/browser/prefetch/cache.ts +113 -21
- package/src/browser/prefetch/fetch.ts +156 -18
- package/src/browser/prefetch/queue.ts +36 -5
- package/src/browser/react/Link.tsx +72 -8
- package/src/browser/react/NavigationProvider.tsx +14 -3
- package/src/browser/react/context.ts +7 -2
- package/src/browser/react/use-handle.ts +9 -58
- package/src/browser/react/use-navigation.ts +22 -2
- package/src/browser/react/use-params.ts +11 -1
- package/src/browser/react/use-router.ts +21 -8
- package/src/browser/rsc-router.tsx +26 -3
- package/src/browser/scroll-restoration.ts +10 -8
- package/src/browser/segment-reconciler.ts +36 -14
- package/src/browser/server-action-bridge.ts +8 -18
- package/src/browser/types.ts +20 -5
- package/src/build/generate-manifest.ts +6 -6
- package/src/build/generate-route-types.ts +3 -0
- package/src/build/route-trie.ts +50 -24
- package/src/build/route-types/include-resolution.ts +8 -1
- package/src/build/route-types/router-processing.ts +211 -72
- package/src/build/route-types/scan-filter.ts +8 -1
- package/src/client.tsx +84 -230
- package/src/deps/browser.ts +0 -1
- package/src/handle.ts +40 -0
- package/src/index.rsc.ts +3 -1
- package/src/index.ts +46 -6
- package/src/prerender/store.ts +5 -4
- package/src/prerender.ts +138 -77
- package/src/reverse.ts +25 -1
- package/src/route-definition/dsl-helpers.ts +194 -32
- package/src/route-definition/helpers-types.ts +61 -14
- package/src/route-definition/index.ts +3 -0
- package/src/route-definition/redirect.ts +9 -1
- package/src/route-definition/resolve-handler-use.ts +149 -0
- package/src/route-types.ts +18 -0
- package/src/router/content-negotiation.ts +100 -1
- package/src/router/handler-context.ts +51 -15
- package/src/router/intercept-resolution.ts +9 -4
- package/src/router/lazy-includes.ts +5 -5
- package/src/router/loader-resolution.ts +150 -21
- package/src/router/manifest.ts +22 -13
- package/src/router/match-api.ts +124 -189
- package/src/router/match-middleware/cache-lookup.ts +28 -8
- package/src/router/match-middleware/segment-resolution.ts +53 -0
- package/src/router/match-result.ts +82 -4
- package/src/router/middleware-types.ts +0 -6
- package/src/router/middleware.ts +0 -3
- package/src/router/navigation-snapshot.ts +182 -0
- package/src/router/prerender-match.ts +110 -10
- package/src/router/preview-match.ts +30 -102
- package/src/router/request-classification.ts +310 -0
- package/src/router/route-snapshot.ts +245 -0
- package/src/router/router-interfaces.ts +36 -4
- package/src/router/router-options.ts +37 -11
- package/src/router/segment-resolution/fresh.ts +70 -5
- package/src/router/segment-resolution/revalidation.ts +87 -9
- package/src/router.ts +53 -5
- package/src/rsc/handler.ts +472 -397
- package/src/rsc/loader-fetch.ts +18 -3
- package/src/rsc/manifest-init.ts +5 -1
- package/src/rsc/progressive-enhancement.ts +14 -3
- package/src/rsc/rsc-rendering.ts +15 -2
- package/src/rsc/server-action.ts +10 -2
- package/src/rsc/ssr-setup.ts +2 -2
- package/src/rsc/types.ts +6 -4
- package/src/segment-content-promise.ts +67 -0
- package/src/segment-loader-promise.ts +122 -0
- package/src/segment-system.tsx +11 -61
- package/src/server/context.ts +65 -5
- package/src/server/handle-store.ts +19 -0
- package/src/server/loader-registry.ts +9 -8
- package/src/server/request-context.ts +132 -13
- package/src/ssr/index.tsx +3 -0
- package/src/static-handler.ts +18 -6
- package/src/types/cache-types.ts +4 -4
- package/src/types/handler-context.ts +17 -11
- package/src/types/loader-types.ts +32 -5
- package/src/types/route-entry.ts +12 -1
- package/src/types/segments.ts +1 -1
- package/src/urls/include-helper.ts +24 -14
- package/src/urls/path-helper-types.ts +39 -6
- package/src/urls/path-helper.ts +47 -12
- package/src/urls/pattern-types.ts +12 -0
- package/src/urls/response-types.ts +16 -6
- package/src/use-loader.tsx +77 -5
- package/src/vite/discovery/bundle-postprocess.ts +30 -33
- package/src/vite/discovery/discover-routers.ts +5 -1
- package/src/vite/discovery/prerender-collection.ts +128 -74
- package/src/vite/discovery/state.ts +13 -4
- package/src/vite/index.ts +4 -0
- package/src/vite/plugin-types.ts +60 -5
- package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
- package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/src/vite/plugins/cloudflare-protocol-stub.ts +214 -0
- package/src/vite/plugins/expose-id-utils.ts +12 -0
- package/src/vite/plugins/expose-ids/handler-transform.ts +30 -0
- package/src/vite/plugins/expose-internal-ids.ts +257 -40
- package/src/vite/plugins/performance-tracks.ts +64 -211
- package/src/vite/plugins/refresh-cmd.ts +88 -26
- package/src/vite/rango.ts +17 -11
- package/src/vite/router-discovery.ts +237 -37
- package/src/vite/utils/prerender-utils.ts +37 -5
- package/src/vite/utils/shared-utils.ts +3 -2
- package/src/browser/debug-channel.ts +0 -93
|
@@ -1,235 +1,88 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* React Performance Tracks —
|
|
2
|
+
* React Performance Tracks — RSDW client patch
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Patches the RSDW client so _debugInfo recovery works for plain-object
|
|
5
|
+
* payloads (our RscPayload shape). Without this, the Server Components
|
|
6
|
+
* track in Chrome DevTools stays empty.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* Each request gets a unique debugId (UUID) to correlate the two sides.
|
|
14
|
-
*
|
|
15
|
-
* Uses globalThis to share state between the Vite plugin (main process)
|
|
16
|
-
* and the RSC handler (RSC module graph) — they run in the same Node.js
|
|
17
|
-
* process but different module evaluation contexts.
|
|
8
|
+
* React's flushComponentPerformance uses splice(0) to empty _debugInfo
|
|
9
|
+
* after resolution, then recovers it from the resolved value — but only
|
|
10
|
+
* for arrays, async iterables, React elements, and lazy types. Since our
|
|
11
|
+
* RscPayload is a plain object, _debugInfo is lost. This patch relaxes
|
|
12
|
+
* the check so _debugInfo is recovered from any object.
|
|
18
13
|
*/
|
|
19
14
|
|
|
20
15
|
import type { Plugin } from "vite";
|
|
16
|
+
import { readFile } from "node:fs/promises";
|
|
21
17
|
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
const DEBUG_C2S_EVENT = "rango:perf-c2s";
|
|
18
|
+
const RSDW_PATCH_RE =
|
|
19
|
+
/((?:var|let|const)\s+\w+\s*=\s*root\._children\s*,\s*(\w+)\s*=\s*root\._debugInfo\s*[;,])/;
|
|
25
20
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
21
|
+
function buildPatchReplacement(match: string, debugInfoVar: string): string {
|
|
22
|
+
return `${match}
|
|
23
|
+
if (${debugInfoVar} && 0 === ${debugInfoVar}.length && "fulfilled" === root.status) {
|
|
24
|
+
var _resolved = "function" === typeof resolveLazy ? resolveLazy(root.value) : root.value;
|
|
25
|
+
if ("object" === typeof _resolved && null !== _resolved && isArrayImpl(_resolved._debugInfo)) {
|
|
26
|
+
${debugInfoVar} = _resolved._debugInfo;
|
|
27
|
+
}
|
|
28
|
+
}`;
|
|
34
29
|
}
|
|
35
30
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
sessions: Map<string, DebugSession>;
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
const GLOBAL_KEY = "__RANGO_DEBUG_CHANNELS__";
|
|
31
|
+
export function patchRsdwClientDebugInfoRecovery(code: string): {
|
|
32
|
+
code: string;
|
|
33
|
+
debugInfoVar: string | null;
|
|
34
|
+
} {
|
|
35
|
+
const match = code.match(RSDW_PATCH_RE);
|
|
36
|
+
if (!match) {
|
|
37
|
+
return { code, debugInfoVar: null };
|
|
38
|
+
}
|
|
48
39
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
function getRegistry(): DebugChannelRegistry {
|
|
54
|
-
return ((Module as any)[GLOBAL_KEY] ??= {
|
|
55
|
-
channels: new Map(),
|
|
56
|
-
sessions: new Map(),
|
|
57
|
-
});
|
|
40
|
+
return {
|
|
41
|
+
code: code.replace(match[1]!, buildPatchReplacement(match[1]!, match[2]!)),
|
|
42
|
+
debugInfoVar: match[2]!,
|
|
43
|
+
};
|
|
58
44
|
}
|
|
59
45
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
registry.channels.size,
|
|
84
|
-
);
|
|
85
|
-
return null;
|
|
46
|
+
export function performanceTracksOptimizeDepsPlugin(): {
|
|
47
|
+
name: string;
|
|
48
|
+
setup(build: any): void;
|
|
49
|
+
} {
|
|
50
|
+
return {
|
|
51
|
+
name: "@rangojs/router:performance-tracks-optimize-deps",
|
|
52
|
+
setup(build: any): void {
|
|
53
|
+
build.onLoad(
|
|
54
|
+
{
|
|
55
|
+
filter:
|
|
56
|
+
/react-server-dom-webpack-client\.browser\.(development|production)\.js$/,
|
|
57
|
+
},
|
|
58
|
+
async (args: { path: string }) => {
|
|
59
|
+
const code = await readFile(args.path, "utf8");
|
|
60
|
+
const patched = patchRsdwClientDebugInfoRecovery(code);
|
|
61
|
+
return {
|
|
62
|
+
contents: patched.code,
|
|
63
|
+
loader: "js",
|
|
64
|
+
};
|
|
65
|
+
},
|
|
66
|
+
);
|
|
67
|
+
},
|
|
68
|
+
};
|
|
86
69
|
}
|
|
87
70
|
|
|
88
|
-
const bytesToBase64 = (bytes: Uint8Array) =>
|
|
89
|
-
Buffer.from(bytes).toString("base64");
|
|
90
|
-
|
|
91
|
-
const base64ToBytes = (base64: string) =>
|
|
92
|
-
new Uint8Array(Buffer.from(base64, "base64"));
|
|
93
|
-
|
|
94
71
|
export function performanceTracksPlugin(): Plugin {
|
|
95
72
|
return {
|
|
96
73
|
name: "@rangojs/router:performance-tracks",
|
|
97
|
-
// Only configureServer hook — naturally dev-only
|
|
98
|
-
|
|
99
|
-
configureServer(server) {
|
|
100
|
-
console.log("[perf-tracks] plugin loaded, configureServer called");
|
|
101
|
-
const hot = server.environments.client.hot;
|
|
102
|
-
const registry = getRegistry();
|
|
103
|
-
const sessions = registry.sessions;
|
|
104
|
-
|
|
105
|
-
const sendChunk = (debugId: string, chunk: Uint8Array) => {
|
|
106
|
-
hot.send(DEBUG_S2C_EVENT, {
|
|
107
|
-
i: debugId,
|
|
108
|
-
b: bytesToBase64(chunk),
|
|
109
|
-
} satisfies DebugPayload);
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
const cleanupIfEnded = (debugId: string, session: DebugSession) => {
|
|
113
|
-
if (session.pendingChunks || !session.ended) return;
|
|
114
|
-
sessions.delete(debugId);
|
|
115
|
-
hot.send(DEBUG_S2C_EVENT, {
|
|
116
|
-
i: debugId,
|
|
117
|
-
d: true,
|
|
118
|
-
} satisfies DebugPayload);
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
const registerDebugChannel = (debugId: string) => {
|
|
122
|
-
let session = sessions.get(debugId);
|
|
123
|
-
if (!session) {
|
|
124
|
-
session = { pendingChunks: [], ended: false };
|
|
125
|
-
sessions.set(debugId, session);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Readable: receives client-to-server commands via WS
|
|
129
|
-
const readable = new ReadableStream<Uint8Array>({
|
|
130
|
-
start(controller) {
|
|
131
|
-
session!.cmdController = controller;
|
|
132
|
-
},
|
|
133
|
-
cancel() {
|
|
134
|
-
delete session!.cmdController;
|
|
135
|
-
},
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
// Writable: React writes debug data here, we forward to client via WS
|
|
139
|
-
const writable = new WritableStream<Uint8Array>({
|
|
140
|
-
write(chunk) {
|
|
141
|
-
if (session!.pendingChunks) {
|
|
142
|
-
session!.pendingChunks.push(chunk);
|
|
143
|
-
} else {
|
|
144
|
-
sendChunk(debugId, chunk);
|
|
145
|
-
}
|
|
146
|
-
},
|
|
147
|
-
close() {
|
|
148
|
-
session!.ended = true;
|
|
149
|
-
cleanupIfEnded(debugId, session!);
|
|
150
|
-
},
|
|
151
|
-
abort() {
|
|
152
|
-
session!.ended = true;
|
|
153
|
-
cleanupIfEnded(debugId, session!);
|
|
154
|
-
},
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
// Store on globalThis so the RSC handler can retrieve it
|
|
158
|
-
registry.channels.set(debugId, { readable, writable });
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
// Listen for client-to-server debug messages
|
|
162
|
-
// Payload shapes: { i, d: true } (done), { i, b } (chunk), { i } (ready)
|
|
163
|
-
hot.on(DEBUG_C2S_EVENT, (raw: unknown) => {
|
|
164
|
-
const payload = raw as { i: string; b?: string; d?: true };
|
|
165
|
-
const session = sessions.get(payload.i);
|
|
166
|
-
|
|
167
|
-
if (payload.d) {
|
|
168
|
-
if (session?.cmdController) {
|
|
169
|
-
try {
|
|
170
|
-
session.cmdController.close();
|
|
171
|
-
} catch {
|
|
172
|
-
// ignore
|
|
173
|
-
}
|
|
174
|
-
delete session.cmdController;
|
|
175
|
-
}
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
if (payload.b) {
|
|
180
|
-
if (session?.cmdController) {
|
|
181
|
-
try {
|
|
182
|
-
session.cmdController.enqueue(base64ToBytes(payload.b));
|
|
183
|
-
} catch {
|
|
184
|
-
delete session!.cmdController;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
return;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// Ready signal — flush pending chunks
|
|
191
|
-
if (session) {
|
|
192
|
-
if (session.pendingChunks) {
|
|
193
|
-
for (const chunk of session.pendingChunks) {
|
|
194
|
-
sendChunk(payload.i, chunk);
|
|
195
|
-
}
|
|
196
|
-
delete session.pendingChunks;
|
|
197
|
-
}
|
|
198
|
-
cleanupIfEnded(payload.i, session);
|
|
199
|
-
} else {
|
|
200
|
-
sessions.set(payload.i, { ended: false });
|
|
201
|
-
}
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
// Register middleware directly (not as post-hook) so it runs
|
|
205
|
-
// BEFORE the RSC handler — the channel must exist before rendering.
|
|
206
|
-
server.middlewares.use((req: any, _res: any, next: any) => {
|
|
207
|
-
const existingId = req.headers[DEBUG_ID_HEADER.toLowerCase()] as string;
|
|
208
|
-
const isHtml = req.headers.accept?.includes("text/html");
|
|
209
|
-
|
|
210
|
-
if (!existingId && !isHtml) {
|
|
211
|
-
next();
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
const debugId = existingId || crypto.randomUUID();
|
|
216
|
-
if (!existingId) {
|
|
217
|
-
const lowerName = DEBUG_ID_HEADER.toLowerCase();
|
|
218
|
-
req.headers[lowerName] = debugId;
|
|
219
|
-
if (req.rawHeaders) {
|
|
220
|
-
req.rawHeaders.push(DEBUG_ID_HEADER, debugId);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
74
|
|
|
224
|
-
|
|
75
|
+
transform(code, id) {
|
|
76
|
+
if (!id.includes("react-server-dom") || !id.includes("client")) return;
|
|
77
|
+
const patched = patchRsdwClientDebugInfoRecovery(code);
|
|
78
|
+
if (!patched.debugInfoVar) return;
|
|
79
|
+
if (process.env.INTERNAL_RANGO_DEBUG)
|
|
225
80
|
console.log(
|
|
226
|
-
"[perf-tracks]
|
|
227
|
-
|
|
228
|
-
"
|
|
229
|
-
existingId ? "client header" : "SSR inject",
|
|
81
|
+
"[perf-tracks] patched RSDW client (var:",
|
|
82
|
+
patched.debugInfoVar,
|
|
83
|
+
")",
|
|
230
84
|
);
|
|
231
|
-
|
|
232
|
-
});
|
|
85
|
+
return patched.code;
|
|
233
86
|
},
|
|
234
87
|
};
|
|
235
88
|
}
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import type { Plugin } from "vite";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Vite plugin that triggers a full browser reload
|
|
5
|
-
*
|
|
4
|
+
* Vite plugin that triggers a full browser reload from terminal input.
|
|
5
|
+
*
|
|
6
|
+
* This plugin is intentionally passive:
|
|
7
|
+
* - it never enables raw mode on stdin
|
|
8
|
+
* - it never restores terminal state
|
|
9
|
+
* - it reacts to Ctrl+R when that raw byte reaches the process
|
|
10
|
+
* - it also supports safe line-based fallbacks like "e" + Enter
|
|
6
11
|
*
|
|
7
12
|
* Usage:
|
|
8
13
|
* ```ts
|
|
@@ -20,35 +25,95 @@ export function poke(): Plugin {
|
|
|
20
25
|
|
|
21
26
|
configureServer(server) {
|
|
22
27
|
const stdin = process.stdin;
|
|
28
|
+
const debug = process.env.RANGO_POKE_DEBUG === "1";
|
|
29
|
+
|
|
30
|
+
const triggerReload = (source: string) => {
|
|
31
|
+
server.hot.send({ type: "full-reload", path: "*" });
|
|
32
|
+
server.config.logger.info(` browser reload (${source})`, {
|
|
33
|
+
timestamp: true,
|
|
34
|
+
});
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const toBuffer = (chunk: string | Buffer): Buffer => {
|
|
38
|
+
return typeof chunk === "string" ? Buffer.from(chunk, "utf8") : chunk;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const formatChunk = (chunk: string | Buffer): string => {
|
|
42
|
+
const data = toBuffer(chunk);
|
|
43
|
+
const hex = Array.from(data)
|
|
44
|
+
.map((byte) => `0x${byte.toString(16).padStart(2, "0")}`)
|
|
45
|
+
.join(" ");
|
|
46
|
+
const ascii = Array.from(data)
|
|
47
|
+
.map((byte) => {
|
|
48
|
+
if (byte >= 0x20 && byte <= 0x7e) return String.fromCharCode(byte);
|
|
49
|
+
if (byte === 0x0a) return "\\n";
|
|
50
|
+
if (byte === 0x0d) return "\\r";
|
|
51
|
+
if (byte === 0x09) return "\\t";
|
|
52
|
+
return ".";
|
|
53
|
+
})
|
|
54
|
+
.join("");
|
|
55
|
+
return `len=${data.length} hex=[${hex}] ascii="${ascii}"`;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const readCtrlR = (chunk: string | Buffer): boolean => {
|
|
59
|
+
const data =
|
|
60
|
+
typeof chunk === "string" ? Buffer.from(chunk, "utf8") : chunk;
|
|
61
|
+
return data.length === 1 && data[0] === 0x12;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const readSubmittedCommands = (chunk: string | Buffer): string[] => {
|
|
65
|
+
const text = toBuffer(chunk)
|
|
66
|
+
.toString("utf8")
|
|
67
|
+
.replace(/\r\n/g, "\n")
|
|
68
|
+
.replace(/\r/g, "\n");
|
|
69
|
+
|
|
70
|
+
if (!text.includes("\n")) return [];
|
|
71
|
+
|
|
72
|
+
const lines = text.split("\n");
|
|
73
|
+
lines.pop();
|
|
74
|
+
return lines;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
if (debug) {
|
|
78
|
+
server.config.logger.info(
|
|
79
|
+
` poke debug enabled (isTTY=${stdin.isTTY ? "yes" : "no"}, isRaw=${stdin.isTTY ? (stdin.isRaw ? "yes" : "no") : "n/a"})`,
|
|
80
|
+
{ timestamp: true },
|
|
81
|
+
);
|
|
82
|
+
}
|
|
23
83
|
|
|
24
|
-
// Raw mode delivers individual keystrokes as immediate single-byte
|
|
25
|
-
// events instead of waiting for Enter (cooked/line-buffered mode).
|
|
26
|
-
// Without it, Ctrl+R (0x12) is never delivered as a discrete byte.
|
|
27
|
-
// When stdin is a pipe (CI, spawned process) setRawMode is unavailable
|
|
28
|
-
// but data already arrives unbuffered, so the isTTY guard suffices.
|
|
29
|
-
const previousRawMode = stdin.isTTY ? stdin.isRaw : null;
|
|
30
84
|
if (stdin.isTTY) {
|
|
31
|
-
|
|
85
|
+
server.config.logger.info(
|
|
86
|
+
" poke ready: press e + enter to reload browser (ctrl+r also works when available)",
|
|
87
|
+
{ timestamp: true },
|
|
88
|
+
);
|
|
32
89
|
}
|
|
33
90
|
|
|
34
|
-
const onData = (data: Buffer) => {
|
|
35
|
-
if (
|
|
91
|
+
const onData = (data: string | Buffer) => {
|
|
92
|
+
if (debug) {
|
|
93
|
+
server.config.logger.info(` poke stdin ${formatChunk(data)}`, {
|
|
94
|
+
timestamp: true,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
36
97
|
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
if (data
|
|
42
|
-
|
|
98
|
+
// Only react to the exact Ctrl+R byte when some host terminal or
|
|
99
|
+
// wrapper already delivers it to this process. We intentionally do
|
|
100
|
+
// not enable raw mode here because that can steal Vite shortcuts
|
|
101
|
+
// like "r" / "q" and interfere with terminal-level controls.
|
|
102
|
+
if (readCtrlR(data)) {
|
|
103
|
+
triggerReload("ctrl+r");
|
|
43
104
|
return;
|
|
44
105
|
}
|
|
45
106
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
107
|
+
for (const command of readSubmittedCommands(data)) {
|
|
108
|
+
if (command === "e") {
|
|
109
|
+
triggerReload("e+enter");
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (command === "\u001br") {
|
|
114
|
+
triggerReload("option+r+enter");
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
52
117
|
}
|
|
53
118
|
};
|
|
54
119
|
|
|
@@ -56,9 +121,6 @@ export function poke(): Plugin {
|
|
|
56
121
|
|
|
57
122
|
server.httpServer?.on("close", () => {
|
|
58
123
|
stdin.off("data", onData);
|
|
59
|
-
if (stdin.isTTY && previousRawMode !== null) {
|
|
60
|
-
stdin.setRawMode(previousRawMode);
|
|
61
|
-
}
|
|
62
124
|
});
|
|
63
125
|
},
|
|
64
126
|
};
|
package/src/vite/rango.ts
CHANGED
|
@@ -55,14 +55,22 @@ import { performanceTracksPlugin } from "./plugins/performance-tracks.js";
|
|
|
55
55
|
export async function rango(options?: RangoOptions): Promise<PluginOption[]> {
|
|
56
56
|
const resolvedOptions: RangoOptions = options ?? { preset: "node" };
|
|
57
57
|
const preset = resolvedOptions.preset ?? "node";
|
|
58
|
-
console.log("[perf-tracks] rango() called, preset:", preset);
|
|
59
58
|
const showBanner = resolvedOptions.banner ?? true;
|
|
60
59
|
|
|
61
60
|
const plugins: PluginOption[] = [];
|
|
62
61
|
|
|
63
62
|
// Get package resolution info (workspace vs npm install)
|
|
64
63
|
const rangoAliases = getPackageAliases();
|
|
65
|
-
const excludeDeps =
|
|
64
|
+
const excludeDeps = [
|
|
65
|
+
...getExcludeDeps(),
|
|
66
|
+
// The public browser entry re-exports the RSDW browser client.
|
|
67
|
+
// Excluding both keeps Vite from freezing the unpatched bundle into
|
|
68
|
+
// .vite/deps before our source transforms run.
|
|
69
|
+
"@vitejs/plugin-rsc/browser",
|
|
70
|
+
// Keep the browser RSDW client out of Vite's dep optimizer so our
|
|
71
|
+
// cjs-to-esm transform can patch the real file.
|
|
72
|
+
"@vitejs/plugin-rsc/vendor/react-server-dom/client.browser",
|
|
73
|
+
];
|
|
66
74
|
|
|
67
75
|
// Mutable ref for router path (node preset only).
|
|
68
76
|
// Set immediately when user-specified, or populated by the auto-discover
|
|
@@ -184,6 +192,9 @@ export async function rango(options?: RangoOptions): Promise<PluginOption[]> {
|
|
|
184
192
|
|
|
185
193
|
plugins.push(createVirtualEntriesPlugin(finalEntries));
|
|
186
194
|
|
|
195
|
+
// Dev-only: RSDW client patch for React Performance Tracks
|
|
196
|
+
plugins.push(performanceTracksPlugin());
|
|
197
|
+
|
|
187
198
|
// Add RSC plugin with cloudflare-specific options
|
|
188
199
|
// Note: loadModuleDevProxy should NOT be used with childEnvironments
|
|
189
200
|
// since SSR runs in workerd alongside RSC
|
|
@@ -336,14 +347,8 @@ export async function rango(options?: RangoOptions): Promise<PluginOption[]> {
|
|
|
336
347
|
// Add virtual entries plugin (RSC entry generated lazily from routerRef)
|
|
337
348
|
plugins.push(createVirtualEntriesPlugin(finalEntries, routerRef));
|
|
338
349
|
|
|
339
|
-
// Dev-only:
|
|
340
|
-
|
|
341
|
-
const perfPlugin = performanceTracksPlugin();
|
|
342
|
-
console.log(
|
|
343
|
-
"[perf-tracks] rango: plugin created, has configureServer:",
|
|
344
|
-
!!perfPlugin.configureServer,
|
|
345
|
-
);
|
|
346
|
-
plugins.push(perfPlugin);
|
|
350
|
+
// Dev-only: RSDW client patch for React Performance Tracks
|
|
351
|
+
plugins.push(performanceTracksPlugin());
|
|
347
352
|
|
|
348
353
|
plugins.push(
|
|
349
354
|
rsc({
|
|
@@ -448,7 +453,8 @@ export async function rango(options?: RangoOptions): Promise<PluginOption[]> {
|
|
|
448
453
|
createRouterDiscoveryPlugin(discoveryEntryPath, {
|
|
449
454
|
routerPathRef: discoveryRouterRef,
|
|
450
455
|
enableBuildPrerender: prerenderEnabled,
|
|
451
|
-
|
|
456
|
+
buildEnv: options?.buildEnv,
|
|
457
|
+
preset,
|
|
452
458
|
}),
|
|
453
459
|
);
|
|
454
460
|
|