@rangojs/router 0.10.1 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/testing/vitest.js +1 -1
- package/dist/types/browser/partial-update.d.ts +1 -0
- package/dist/types/client-urls/navigation.d.ts +11 -0
- package/dist/types/client-urls/revalidate-chain.d.ts +33 -0
- package/dist/types/client-urls/types.d.ts +41 -14
- package/dist/types/client.d.ts +2 -0
- package/dist/types/deps/rsc-client.d.ts +1 -0
- package/dist/types/deps/rsc.d.ts +1 -1
- package/dist/types/deps/ssr.d.ts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.rsc.d.ts +1 -1
- package/dist/types/router/is-action.d.ts +34 -0
- package/dist/types/rsc/types.d.ts +9 -9
- package/dist/types/ssr/index.d.ts +27 -4
- package/dist/types/testing/flight.d.ts +4 -3
- package/dist/types/testing/index.d.ts +3 -1
- package/dist/types/testing/run-client-revalidate.d.ts +43 -0
- package/dist/types/testing/to-url.d.ts +2 -0
- package/dist/types/testing/vitest-stubs/plugin-rsc.d.ts +2 -0
- package/dist/types/testing/vitest.d.ts +2 -1
- package/dist/types/types/handler-context.d.ts +12 -5
- package/dist/types/types/index.d.ts +1 -1
- package/dist/types/vite/plugins/expose-action-id.d.ts +14 -0
- package/dist/types/vite/plugins/virtual-entries.d.ts +3 -2
- package/dist/vite/index.js +66 -13
- package/package.json +8 -3
- package/skills/client-urls/SKILL.md +26 -4
- package/skills/loader/SKILL.md +1 -0
- package/skills/testing/SKILL.md +1 -0
- package/skills/testing/setup.md +6 -6
- package/skills/typesafety/route-types.md +1 -0
- package/src/browser/partial-update.ts +11 -2
- package/src/browser/server-action-bridge.ts +9 -2
- package/src/cache/cache-runtime.ts +1 -1
- package/src/cache/segment-codec.ts +2 -2
- package/src/client-urls/navigation.ts +40 -42
- package/src/client-urls/revalidate-chain.ts +83 -0
- package/src/client-urls/types.ts +41 -13
- package/src/client.tsx +5 -0
- package/src/deps/rsc-client.ts +8 -0
- package/src/deps/rsc.ts +4 -2
- package/src/deps/ssr.ts +1 -0
- package/src/index.rsc.ts +1 -0
- package/src/index.ts +1 -0
- package/src/router/is-action.ts +100 -0
- package/src/router/revalidation.ts +5 -48
- package/src/rsc/handler.ts +3 -3
- package/src/rsc/server-action.ts +2 -4
- package/src/rsc/types.ts +9 -9
- package/src/ssr/index.tsx +132 -51
- package/src/testing/flight.ts +4 -3
- package/src/testing/index.ts +4 -1
- package/src/testing/run-client-revalidate.ts +108 -0
- package/src/testing/run-transition-when.ts +1 -3
- package/src/testing/to-url.ts +5 -0
- package/src/testing/vitest-stubs/plugin-rsc.ts +13 -5
- package/src/testing/vitest.ts +3 -2
- package/src/types/handler-context.ts +13 -5
- package/src/types/index.ts +1 -0
- package/src/vite/plugins/expose-action-id.ts +29 -1
- package/src/vite/plugins/use-cache-transform.ts +65 -1
- package/src/vite/plugins/virtual-entries.ts +14 -11
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import type { Plugin } from "vite";
|
|
20
|
+
import type { ModuleExportMeta } from "@vitejs/plugin-rsc/transforms";
|
|
20
21
|
import path from "node:path";
|
|
21
22
|
import MagicString from "magic-string";
|
|
22
23
|
import { normalizePath, hashId } from "./expose-id-utils.js";
|
|
@@ -89,6 +90,13 @@ export function useCacheTransform(): Plugin {
|
|
|
89
90
|
return;
|
|
90
91
|
}
|
|
91
92
|
|
|
93
|
+
// plugin-rsc 0.5.34 `matchDirective` does `stmt.directive.match(...)`
|
|
94
|
+
// after `"directive" in node`. Vite/oxc parseAst now emits
|
|
95
|
+
// `directive: null` on ordinary ExpressionStatements, so a file that
|
|
96
|
+
// mixes a `"use cache"` function with a sibling handler whose first
|
|
97
|
+
// statement is an expression throws and the wrap is dropped.
|
|
98
|
+
stripNullDirectiveFields(ast);
|
|
99
|
+
|
|
92
100
|
const filePath = normalizePath(path.relative(projectRoot, id));
|
|
93
101
|
const isLayoutOrTemplate = LAYOUT_TEMPLATE_PATTERN.test(id);
|
|
94
102
|
|
|
@@ -101,6 +109,7 @@ export function useCacheTransform(): Plugin {
|
|
|
101
109
|
isBuild,
|
|
102
110
|
isLayoutOrTemplate,
|
|
103
111
|
transformWrapExport,
|
|
112
|
+
hasDirective,
|
|
104
113
|
);
|
|
105
114
|
}
|
|
106
115
|
|
|
@@ -131,6 +140,7 @@ function transformFileLevelUseCache(
|
|
|
131
140
|
isBuild: boolean,
|
|
132
141
|
isLayoutOrTemplate: boolean,
|
|
133
142
|
transformWrapExport: (typeof import("@vitejs/plugin-rsc/transforms"))["transformWrapExport"],
|
|
143
|
+
hasDirective: (typeof import("@vitejs/plugin-rsc/transforms"))["hasDirective"],
|
|
134
144
|
) {
|
|
135
145
|
const unconfirmedExports: string[] = [];
|
|
136
146
|
|
|
@@ -140,8 +150,18 @@ function transformFileLevelUseCache(
|
|
|
140
150
|
return `__rango_registerCachedFunction(${value}, ${JSON.stringify(funcId)}, "default")`;
|
|
141
151
|
},
|
|
142
152
|
rejectNonAsyncFunction: false,
|
|
143
|
-
filter: (name: string, meta:
|
|
153
|
+
filter: (name: string, meta: ModuleExportMeta) => {
|
|
144
154
|
if (name === "default" && isLayoutOrTemplate) return false;
|
|
155
|
+
// plugin-rsc 0.5.34 hoists mixed inline `"use server"` out of a
|
|
156
|
+
// file-level `"use cache"` module as `$$hoist_*` exports and rebinds the
|
|
157
|
+
// original name to `registerServerReference($$hoist_*, ...)`. Both are
|
|
158
|
+
// server references, not cached functions. The directive check covers
|
|
159
|
+
// the pre-hoist shape (this plugin seeing the source first).
|
|
160
|
+
if (name.startsWith("$$hoist_")) return false;
|
|
161
|
+
if (isHoistedServerReferenceRebind(meta.valueNode)) return false;
|
|
162
|
+
if (functionHasUseServerDirective(meta.valueNode, hasDirective)) {
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
145
165
|
// isFunction is boolean | undefined: true = confirmed function, false =
|
|
146
166
|
// confirmed non-function, undefined = cannot tell statically (e.g. a
|
|
147
167
|
// factory/HOF initializer `const x = makeCached(fn)`). Deliberate policy:
|
|
@@ -250,6 +270,50 @@ function transformFunctionLevelUseCache(
|
|
|
250
270
|
}
|
|
251
271
|
}
|
|
252
272
|
|
|
273
|
+
function stripNullDirectiveFields(node: unknown): void {
|
|
274
|
+
if (!node || typeof node !== "object") return;
|
|
275
|
+
const rec = node as Record<string, unknown>;
|
|
276
|
+
if (rec.type === "ExpressionStatement" && typeof rec.directive !== "string") {
|
|
277
|
+
delete rec.directive;
|
|
278
|
+
}
|
|
279
|
+
for (const value of Object.values(rec)) {
|
|
280
|
+
if (Array.isArray(value)) {
|
|
281
|
+
for (const item of value) stripNullDirectiveFields(item);
|
|
282
|
+
} else if (value && typeof value === "object" && "type" in value) {
|
|
283
|
+
stripNullDirectiveFields(value);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function isHoistedServerReferenceRebind(
|
|
289
|
+
valueNode: ModuleExportMeta["valueNode"],
|
|
290
|
+
): boolean {
|
|
291
|
+
if (!valueNode || valueNode.type !== "CallExpression") return false;
|
|
292
|
+
const first = valueNode.arguments[0];
|
|
293
|
+
return (
|
|
294
|
+
first !== undefined &&
|
|
295
|
+
first.type === "Identifier" &&
|
|
296
|
+
first.name.startsWith("$$hoist_")
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function functionHasUseServerDirective(
|
|
301
|
+
valueNode: ModuleExportMeta["valueNode"],
|
|
302
|
+
hasDirective: (typeof import("@vitejs/plugin-rsc/transforms"))["hasDirective"],
|
|
303
|
+
): boolean {
|
|
304
|
+
if (!valueNode || !("body" in valueNode)) return false;
|
|
305
|
+
const { body } = valueNode;
|
|
306
|
+
if (!body || Array.isArray(body) || body.type !== "BlockStatement") {
|
|
307
|
+
return false;
|
|
308
|
+
}
|
|
309
|
+
// plugin-rsc types valueNode with plain estree nodes but hasDirective with
|
|
310
|
+
// its oxc-flavored AST; the flavors differ only in position/extra fields.
|
|
311
|
+
return hasDirective(
|
|
312
|
+
body.body as Parameters<typeof hasDirective>[0],
|
|
313
|
+
"use server",
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
|
|
253
317
|
function findFileLevelDirective(
|
|
254
318
|
ast: any,
|
|
255
319
|
): { start: number; end: number } | null {
|
|
@@ -63,18 +63,20 @@ function emitProgressiveChunkSize(value: number): string {
|
|
|
63
63
|
/**
|
|
64
64
|
* Generate the virtual SSR entry. `headScripts` mirrors the rango() plugin
|
|
65
65
|
* option: "preinit" (default) installs the client-reference preinit hook and
|
|
66
|
-
*
|
|
67
|
-
* "preload" omits the hook and
|
|
66
|
+
* threads `getClientEntryUrl` so Fizz emits `bootstrapModules`;
|
|
67
|
+
* "preload" omits the hook and uses the deprecated inline
|
|
68
|
+
* `loadBootstrapScriptContent` bootstrap.
|
|
68
69
|
*/
|
|
69
70
|
export function getVirtualEntrySSR(
|
|
70
71
|
headScripts: HeadScriptsOption = "preinit",
|
|
71
72
|
progressiveChunkSize?: number,
|
|
72
73
|
): string {
|
|
73
74
|
const preinit = headScripts !== "preload";
|
|
74
|
-
// The preload variant drops
|
|
75
|
-
// here so the template below stays a single
|
|
75
|
+
// The preload variant drops the preinit-only imports/install and swaps the
|
|
76
|
+
// bootstrap dep, all built here so the template below stays a single
|
|
77
|
+
// unconditional shape.
|
|
76
78
|
const depsImportNames = preinit
|
|
77
|
-
? "createFromReadableStream,\n setOnClientReference,"
|
|
79
|
+
? "createFromReadableStream,\n setOnClientReference,\n getClientEntryUrl,"
|
|
78
80
|
: "createFromReadableStream,";
|
|
79
81
|
const ssrImportNames = preinit ? "\n installClientReferencePreinit," : "";
|
|
80
82
|
const install = preinit
|
|
@@ -85,6 +87,10 @@ export function getVirtualEntrySSR(
|
|
|
85
87
|
installClientReferencePreinit(setOnClientReference);
|
|
86
88
|
`
|
|
87
89
|
: "";
|
|
90
|
+
const bootstrapDep = preinit
|
|
91
|
+
? "getClientEntryUrl,"
|
|
92
|
+
: `loadBootstrapScriptContent: () =>
|
|
93
|
+
import.meta.viteRsc.loadBootstrapScriptContent("index"),`;
|
|
88
94
|
const hs = JSON.stringify(headScripts);
|
|
89
95
|
// Emitted into all three handlers: live SSR and shell capture consume it
|
|
90
96
|
// directly; the resume handler receives it for dep-shape uniformity (resume()
|
|
@@ -114,8 +120,7 @@ export const renderHTML = createSSRHandler({
|
|
|
114
120
|
renderToReadableStream,
|
|
115
121
|
injectRSCPayload,
|
|
116
122
|
headScripts: ${hs},${pcs}
|
|
117
|
-
|
|
118
|
-
import.meta.viteRsc.loadBootstrapScriptContent("index"),
|
|
123
|
+
${bootstrapDep}
|
|
119
124
|
});
|
|
120
125
|
|
|
121
126
|
export const captureShellHTML = createShellCaptureHandler({
|
|
@@ -125,8 +130,7 @@ export const captureShellHTML = createShellCaptureHandler({
|
|
|
125
130
|
prerender,
|
|
126
131
|
resume,
|
|
127
132
|
headScripts: ${hs},${pcs}
|
|
128
|
-
|
|
129
|
-
import.meta.viteRsc.loadBootstrapScriptContent("index"),
|
|
133
|
+
${bootstrapDep}
|
|
130
134
|
});
|
|
131
135
|
|
|
132
136
|
export const resumeShellHTML = createShellResumeHandler({
|
|
@@ -136,8 +140,7 @@ export const resumeShellHTML = createShellResumeHandler({
|
|
|
136
140
|
prerender,
|
|
137
141
|
resume,
|
|
138
142
|
headScripts: ${hs},${pcs}
|
|
139
|
-
|
|
140
|
-
import.meta.viteRsc.loadBootstrapScriptContent("index"),
|
|
143
|
+
${bootstrapDep}
|
|
141
144
|
});
|
|
142
145
|
`.trim();
|
|
143
146
|
}
|