@rangojs/router 0.24.4 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/bin/rango.js +363 -39
- package/dist/testing/vitest.js +37 -4
- package/dist/types/browser/event-controller.d.ts +24 -5
- package/dist/types/browser/intercept-utils.d.ts +5 -2
- package/dist/types/browser/merge-segment-loaders.d.ts +0 -23
- package/dist/types/browser/navigation-transaction.d.ts +5 -5
- package/dist/types/browser/partial-update.d.ts +21 -13
- package/dist/types/browser/prefetch/cache.d.ts +8 -0
- package/dist/types/browser/rango-marker.d.ts +7 -0
- package/dist/types/browser/react/filter-segment-order.d.ts +10 -0
- package/dist/types/browser/react/use-handle.d.ts +20 -0
- package/dist/types/browser/react/use-href.d.ts +6 -0
- package/dist/types/browser/react/use-router.d.ts +6 -1
- package/dist/types/browser/response-adapter.d.ts +5 -3
- package/dist/types/browser/scroll-restoration.d.ts +9 -3
- package/dist/types/browser/segment-reconciler.d.ts +10 -13
- package/dist/types/browser/types.d.ts +38 -8
- package/dist/types/build/route-types/per-module-writer.d.ts +12 -0
- package/dist/types/cache/cf/cf-shell-frame.d.ts +2 -0
- package/dist/types/cache/types.d.ts +8 -0
- package/dist/types/client-urls/client-urls.d.ts +6 -2
- package/dist/types/client-urls/revalidate-registry.d.ts +1 -1
- package/dist/types/client-urls/server-projection.d.ts +9 -5
- package/dist/types/client-urls/types.d.ts +29 -16
- package/dist/types/client.d.ts +2 -2
- package/dist/types/client.rsc.d.ts +2 -2
- package/dist/types/decode-loader-results.d.ts +28 -7
- package/dist/types/href-client.d.ts +3 -1
- package/dist/types/index.rsc.d.ts +1 -1
- package/dist/types/loader-redirect.d.ts +2 -3
- package/dist/types/outlet-context.d.ts +7 -6
- package/dist/types/prefetch-markers.d.ts +14 -5
- package/dist/types/route-content-wrapper.d.ts +17 -14
- package/dist/types/route-definition/helpers-types.d.ts +7 -2
- package/dist/types/route-definition/resolve-handler-use.d.ts +6 -1
- package/dist/types/route-definition/rewrite.d.ts +48 -5
- package/dist/types/route-types.d.ts +2 -2
- package/dist/types/router/handler-context.d.ts +14 -2
- package/dist/types/router/intercept-resolution.d.ts +19 -46
- package/dist/types/router/lazy-includes.d.ts +7 -0
- package/dist/types/router/match-middleware/intercept-resolution.d.ts +2 -2
- package/dist/types/router/middleware.d.ts +0 -16
- package/dist/types/router/navigation-snapshot.d.ts +2 -0
- package/dist/types/router/prefetch-cache-ttl.d.ts +8 -11
- package/dist/types/router/revalidation.d.ts +8 -1
- package/dist/types/router/router-context.d.ts +4 -16
- package/dist/types/router/router-interfaces.d.ts +0 -6
- package/dist/types/router/router-options.d.ts +4 -7
- package/dist/types/router/segment-resolution/prefetch-deferral.d.ts +31 -8
- package/dist/types/router/segment-resolution/revalidation.d.ts +38 -4
- package/dist/types/router/segment-resolution.d.ts +1 -1
- package/dist/types/router/segment-wrappers.d.ts +3 -5
- package/dist/types/router/substitute-pattern-params.d.ts +3 -2
- package/dist/types/router/tracing.d.ts +2 -2
- package/dist/types/rsc/full-payload.d.ts +14 -2
- package/dist/types/rsc/helpers.d.ts +8 -0
- package/dist/types/rsc/manifest-init.d.ts +20 -0
- package/dist/types/rsc/prefetch-render-error.d.ts +22 -0
- package/dist/types/rsc/progressive-enhancement.d.ts +20 -0
- package/dist/types/rsc/rewrite-passes.d.ts +12 -2
- package/dist/types/rsc/server-action.d.ts +6 -0
- package/dist/types/rsc/shell-capture-constants.d.ts +10 -1
- package/dist/types/rsc/shell-capture.d.ts +7 -0
- package/dist/types/rsc/types.d.ts +5 -0
- package/dist/types/segment-system.d.ts +15 -0
- package/dist/types/server/context.d.ts +64 -4
- package/dist/types/server/request-context.d.ts +41 -4
- package/dist/types/server/request-headers.d.ts +12 -0
- package/dist/types/server/rewrite-trail.d.ts +8 -1
- package/dist/types/testing/e2e/index.d.ts +1 -1
- package/dist/types/testing/flight.entry.d.ts +1 -1
- package/dist/types/testing/index.d.ts +1 -1
- package/dist/types/testing/internal/client-urls-projection.d.ts +4 -3
- package/dist/types/testing/render-route.d.ts +21 -11
- package/dist/types/testing/run-loader.d.ts +7 -0
- package/dist/types/testing/run-middleware.d.ts +7 -0
- package/dist/types/testing/run-revalidate.d.ts +5 -4
- package/dist/types/testing/serve-shell-request.d.ts +41 -4
- package/dist/types/testing/shell-status.d.ts +17 -0
- package/dist/types/testing/vitest-stubs/version.d.ts +1 -0
- package/dist/types/testing/vitest.d.ts +10 -1
- package/dist/types/thenable-status.d.ts +0 -6
- package/dist/types/types/handler-context.d.ts +12 -1
- package/dist/types/types/loader-types.d.ts +4 -3
- package/dist/types/types/route-entry.d.ts +2 -1
- package/dist/types/urls/path-helper-types.d.ts +4 -2
- package/dist/types/vite/discovery/client-urls-projection.d.ts +55 -5
- package/dist/types/vite/discovery/state.d.ts +20 -0
- package/dist/types/vite/plugins/client-urls-revalidate-hoist.d.ts +28 -16
- package/dist/types/vite/plugins/hoist/source.d.ts +8 -7
- package/dist/types/vite/plugins/virtual-entries.d.ts +6 -0
- package/dist/types/vite/utils/prerender-utils.d.ts +7 -0
- package/dist/vite/index.js +6816 -6547
- package/package.json +1 -1
- package/skills/app-structure/SKILL.md +255 -0
- package/skills/app-structure/content-site.md +333 -0
- package/skills/app-structure/e-commerce.md +435 -0
- package/skills/app-structure/large-app.md +885 -0
- package/skills/app-structure/multi-app.md +280 -0
- package/skills/breadcrumbs/SKILL.md +6 -1
- package/skills/caching/SKILL.md +3 -1
- package/skills/catalog.json +12 -0
- package/skills/client-urls/SKILL.md +185 -32
- package/skills/composability/SKILL.md +10 -8
- package/skills/handler-use/SKILL.md +48 -9
- package/skills/hooks/handle-and-actions.md +104 -0
- package/skills/hooks/navigation.md +24 -10
- package/skills/hooks/urls.md +1 -1
- package/skills/i18n/SKILL.md +2 -1
- package/skills/intercept/SKILL.md +60 -29
- package/skills/layout/SKILL.md +6 -3
- package/skills/links/SKILL.md +151 -17
- package/skills/loader/SKILL.md +32 -10
- package/skills/middleware/SKILL.md +111 -15
- package/skills/observability/SKILL.md +11 -0
- package/skills/parallel/SKILL.md +5 -5
- package/skills/ppr/SKILL.md +43 -8
- package/skills/prerender/SKILL.md +1 -2
- package/skills/rango/SKILL.md +3 -0
- package/skills/response-routes/SKILL.md +4 -1
- package/skills/scripts/SKILL.md +12 -1
- package/skills/server-actions/SKILL.md +60 -20
- package/skills/testing/SKILL.md +1 -0
- package/skills/testing/client-components.md +13 -10
- package/skills/testing/loader.md +25 -24
- package/skills/testing/middleware.md +11 -11
- package/skills/testing/response-routes.md +4 -1
- package/skills/testing/serve-shell-request.md +85 -21
- package/skills/ux-architecture/SKILL.md +980 -0
- package/skills/view-transitions/SKILL.md +43 -10
- package/src/__augment-tests__/augmented.check.ts +28 -0
- package/src/browser/event-controller.ts +44 -18
- package/src/browser/intercept-utils.ts +3 -2
- package/src/browser/merge-segment-loaders.ts +0 -85
- package/src/browser/navigation-bridge.ts +112 -84
- package/src/browser/navigation-client.ts +20 -15
- package/src/browser/navigation-store.ts +4 -2
- package/src/browser/navigation-transaction.ts +9 -8
- package/src/browser/partial-update.ts +90 -32
- package/src/browser/prefetch/cache.ts +8 -0
- package/src/browser/prefetch/fetch.ts +13 -3
- package/src/browser/rango-marker.ts +13 -0
- package/src/browser/react/NavigationProvider.tsx +25 -7
- package/src/browser/react/filter-segment-order.ts +13 -0
- package/src/browser/react/use-action.ts +9 -15
- package/src/browser/react/use-handle.ts +63 -71
- package/src/browser/react/use-href.tsx +24 -5
- package/src/browser/react/use-link-status.ts +1 -1
- package/src/browser/react/use-navigation.ts +30 -40
- package/src/browser/react/use-router.ts +37 -6
- package/src/browser/react/use-segments.ts +8 -12
- package/src/browser/response-adapter.ts +11 -5
- package/src/browser/rsc-router.tsx +3 -1
- package/src/browser/scroll-restoration.ts +34 -20
- package/src/browser/segment-reconciler.ts +11 -29
- package/src/browser/server-action-bridge.ts +52 -16
- package/src/browser/types.ts +40 -8
- package/src/build/generate-manifest.ts +12 -1
- package/src/build/route-types/codegen.ts +6 -2
- package/src/build/route-types/per-module-writer.ts +94 -61
- package/src/cache/cache-runtime.ts +9 -0
- package/src/cache/cache-scope.ts +2 -1
- package/src/cache/cf/cf-cache-store.ts +2 -0
- package/src/cache/cf/cf-shell-frame.ts +3 -0
- package/src/cache/types.ts +8 -0
- package/src/cache/vercel/vercel-cache-store.ts +6 -1
- package/src/client-urls/client-urls.ts +202 -153
- package/src/client-urls/revalidate-registry.ts +12 -3
- package/src/client-urls/server-projection.ts +137 -66
- package/src/client-urls/types.ts +30 -15
- package/src/client.rsc.tsx +11 -1
- package/src/client.tsx +9 -1
- package/src/decode-loader-results.ts +64 -31
- package/src/href-client.ts +3 -1
- package/src/index.rsc.ts +1 -0
- package/src/index.ts +12 -4
- package/src/loader-redirect.tsx +2 -3
- package/src/outlet-context.ts +7 -6
- package/src/prefetch-markers.ts +15 -5
- package/src/reverse.ts +3 -3
- package/src/route-content-wrapper.tsx +19 -17
- package/src/route-definition/dsl-helpers.ts +27 -7
- package/src/route-definition/helpers-types.ts +7 -2
- package/src/route-definition/resolve-handler-use.ts +23 -10
- package/src/route-definition/rewrite.ts +86 -19
- package/src/route-types.ts +1 -2
- package/src/router/find-match.ts +37 -1
- package/src/router/handler-context.ts +85 -40
- package/src/router/intercept-resolution.ts +161 -307
- package/src/router/lazy-includes.ts +43 -27
- package/src/router/loader-resolution.ts +21 -1
- package/src/router/manifest.ts +6 -8
- package/src/router/match-api.ts +11 -1
- package/src/router/match-middleware/background-revalidation.ts +2 -4
- package/src/router/match-middleware/cache-lookup.ts +16 -20
- package/src/router/match-middleware/intercept-resolution.ts +41 -60
- package/src/router/match-result.ts +31 -2
- package/src/router/middleware.ts +6 -147
- package/src/router/navigation-snapshot.ts +38 -2
- package/src/router/prefetch-cache-ttl.ts +10 -18
- package/src/router/prerender-match.ts +2 -1
- package/src/router/revalidation.ts +24 -3
- package/src/router/router-context.ts +5 -18
- package/src/router/router-interfaces.ts +0 -7
- package/src/router/router-options.ts +4 -7
- package/src/router/segment-resolution/fresh.ts +6 -2
- package/src/router/segment-resolution/helpers.ts +7 -1
- package/src/router/segment-resolution/prefetch-deferral.ts +62 -12
- package/src/router/segment-resolution/revalidation.ts +75 -35
- package/src/router/segment-resolution.ts +0 -1
- package/src/router/segment-wrappers.ts +8 -13
- package/src/router/substitute-pattern-params.ts +3 -2
- package/src/router/tracing.ts +2 -2
- package/src/router.ts +2 -9
- package/src/rsc/full-payload.ts +26 -4
- package/src/rsc/handler.ts +83 -41
- package/src/rsc/helpers.ts +13 -0
- package/src/rsc/manifest-init.ts +56 -3
- package/src/rsc/prefetch-render-error.ts +38 -0
- package/src/rsc/progressive-enhancement.ts +161 -211
- package/src/rsc/rewrite-passes.ts +56 -9
- package/src/rsc/rsc-rendering.ts +79 -23
- package/src/rsc/server-action.ts +25 -2
- package/src/rsc/shell-capture-constants.ts +22 -0
- package/src/rsc/shell-capture.ts +11 -0
- package/src/rsc/types.ts +5 -0
- package/src/segment-system.tsx +125 -119
- package/src/server/context.ts +96 -5
- package/src/server/handle-store.ts +3 -2
- package/src/server/request-context.ts +42 -3
- package/src/server/request-headers.ts +32 -0
- package/src/server/rewrite-trail.ts +55 -9
- package/src/ssr/ssr-root.tsx +6 -10
- package/src/testing/dispatch.ts +4 -5
- package/src/testing/e2e/index.ts +1 -0
- package/src/testing/flight.entry.ts +1 -0
- package/src/testing/index.ts +1 -0
- package/src/testing/internal/client-urls-projection.ts +31 -11
- package/src/testing/render-route.tsx +83 -40
- package/src/testing/run-loader.ts +26 -2
- package/src/testing/run-middleware.ts +10 -0
- package/src/testing/run-revalidate.ts +13 -6
- package/src/testing/serve-shell-request.ts +77 -4
- package/src/testing/shell-status.ts +40 -0
- package/src/testing/vitest-stubs/version.ts +1 -0
- package/src/testing/vitest.ts +74 -7
- package/src/thenable-status.ts +0 -11
- package/src/types/handler-context.ts +12 -1
- package/src/types/loader-types.ts +4 -3
- package/src/types/route-entry.ts +2 -1
- package/src/urls/include-helper.ts +38 -11
- package/src/urls/path-helper-types.ts +4 -2
- package/src/vite/discovery/client-urls-projection.ts +205 -16
- package/src/vite/discovery/discover-routers.ts +2 -2
- package/src/vite/discovery/prerender-collection.ts +2 -6
- package/src/vite/discovery/state.ts +26 -0
- package/src/vite/plugins/client-urls-revalidate-hoist.ts +190 -77
- package/src/vite/plugins/expose-internal-ids.ts +46 -40
- package/src/vite/plugins/hoist/source.ts +8 -7
- package/src/vite/plugins/transition-when-hoist.ts +1 -0
- package/src/vite/plugins/version-plugin.ts +7 -0
- package/src/vite/plugins/version.d.ts +7 -0
- package/src/vite/plugins/virtual-entries.ts +20 -0
- package/src/vite/rango.ts +4 -1
- package/src/vite/router-discovery.ts +39 -7
- package/src/vite/utils/prerender-utils.ts +17 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
1
2
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
3
|
import { isAbsolute, relative, resolve } from "node:path";
|
|
3
|
-
import { normalizePath, parseAst } from "vite";
|
|
4
|
+
import { normalizePath, parseAst, type EnvironmentModuleNode } from "vite";
|
|
4
5
|
import {
|
|
5
6
|
projectClientUrlsModule,
|
|
6
7
|
type ClientUrlProjection,
|
|
@@ -10,6 +11,9 @@ import {
|
|
|
10
11
|
hashRefKey,
|
|
11
12
|
} from "../plugins/client-ref-hashing.js";
|
|
12
13
|
import { hasUseClientDirective } from "../utils/directive-prologue.js";
|
|
14
|
+
import { firstCodeMatchIndex } from "../../build/route-types/source-scan.js";
|
|
15
|
+
import { parseLang } from "../plugins/hoist/literal.js";
|
|
16
|
+
import type { Node } from "../plugins/hoist/scope.js";
|
|
13
17
|
import { createRangoDebugger, NS } from "../debug.js";
|
|
14
18
|
import { findTsFiles } from "../../build/route-types/scan-filter.js";
|
|
15
19
|
import type { DiscoveryState } from "./state.js";
|
|
@@ -30,6 +34,22 @@ interface ClientUrlProjectionServerModule {
|
|
|
30
34
|
): void;
|
|
31
35
|
}
|
|
32
36
|
|
|
37
|
+
/** The RSC environment whose realm `ClientUrlProjectionServerModule` is. */
|
|
38
|
+
interface ClientUrlRscEnvironment {
|
|
39
|
+
readonly runner: { import(id: string): Promise<unknown> };
|
|
40
|
+
readonly moduleGraph?: {
|
|
41
|
+
getModuleById(id: string): EnvironmentModuleNode | undefined;
|
|
42
|
+
invalidateModule(mod: EnvironmentModuleNode): void;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const CLIENT_URLS_REVALIDATE_QUERY = "rango-revalidate";
|
|
47
|
+
|
|
48
|
+
/** The id of the module registering `file`'s hoisted revalidate() predicates. */
|
|
49
|
+
export function hoistedRevalidateId(file: string): string {
|
|
50
|
+
return `${file}?${CLIENT_URLS_REVALIDATE_QUERY}`;
|
|
51
|
+
}
|
|
52
|
+
|
|
33
53
|
function hasDefaultExport(code: string): boolean {
|
|
34
54
|
let program: { body?: any[] };
|
|
35
55
|
try {
|
|
@@ -106,6 +126,69 @@ export function recordClientUrlsModule(
|
|
|
106
126
|
}
|
|
107
127
|
}
|
|
108
128
|
|
|
129
|
+
/** A module that imports the router's client entry (a cheap pre-filter). */
|
|
130
|
+
export const CLIENT_IMPORT_RE: RegExp = /["']@rangojs\/router\/client["']/;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Whether an edited module can carry a clientUrls() component's own items: it
|
|
134
|
+
* imports "@rangojs/router/client" and assigns a `.use` in code (not in a
|
|
135
|
+
* comment or string). Such a module changes its groups' projections and
|
|
136
|
+
* hoisted revalidate() registrations like a group edit, with or without
|
|
137
|
+
* "use client".
|
|
138
|
+
*/
|
|
139
|
+
export function isClientUrlsComponentUseSource(source: string): boolean {
|
|
140
|
+
return (
|
|
141
|
+
source.includes(".use") &&
|
|
142
|
+
CLIENT_IMPORT_RE.test(source) &&
|
|
143
|
+
firstCodeMatchIndex(source, /\.use\s*=(?!=)/g) >= 0
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* A hash of the text of every `X.use = ...` assignment of a component-use
|
|
149
|
+
* module (isClientUrlsComponentUseSource), or undefined for any other module.
|
|
150
|
+
* Equal before and after a save, the edit (markup, hooks) left what its
|
|
151
|
+
* groups read unchanged: the dev watcher skips re-discovery and the client
|
|
152
|
+
* keeps Fast Refresh. An edit outside the assignments that changes what one
|
|
153
|
+
* means (a helper it calls, an import's source) is not seen. A module that
|
|
154
|
+
* does not parse hashes whole.
|
|
155
|
+
*/
|
|
156
|
+
export function componentUseSignature(
|
|
157
|
+
source: string,
|
|
158
|
+
file: string,
|
|
159
|
+
): string | undefined {
|
|
160
|
+
if (!isClientUrlsComponentUseSource(source)) return undefined;
|
|
161
|
+
const hash = createHash("sha256");
|
|
162
|
+
let program: Node;
|
|
163
|
+
try {
|
|
164
|
+
program = parseAst(source, { lang: parseLang(file) });
|
|
165
|
+
} catch {
|
|
166
|
+
return hash.update(source).digest("base64");
|
|
167
|
+
}
|
|
168
|
+
const visit = (node: unknown): void => {
|
|
169
|
+
if (typeof node !== "object" || node === null) return;
|
|
170
|
+
if (Array.isArray(node)) {
|
|
171
|
+
for (const child of node) visit(child);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
const { type, left } = node as Node;
|
|
175
|
+
if (
|
|
176
|
+
type === "AssignmentExpression" &&
|
|
177
|
+
left.type === "MemberExpression" &&
|
|
178
|
+
!left.computed &&
|
|
179
|
+
left.property.type === "Identifier" &&
|
|
180
|
+
left.property.name === "use"
|
|
181
|
+
) {
|
|
182
|
+
const { start, end } = node as Node;
|
|
183
|
+
hash.update(source.slice(start, end)).update("\0");
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
for (const value of Object.values(node)) visit(value);
|
|
187
|
+
};
|
|
188
|
+
visit(program.body);
|
|
189
|
+
return hash.digest("base64");
|
|
190
|
+
}
|
|
191
|
+
|
|
109
192
|
export function resolveClientUrlsSource(
|
|
110
193
|
state: DiscoveryState,
|
|
111
194
|
referenceId: string,
|
|
@@ -162,14 +245,17 @@ function projectionError(
|
|
|
162
245
|
* import, so `.routes(reference)` materialized the previous pass's projection.
|
|
163
246
|
*
|
|
164
247
|
* Errors are deliberately swallowed per source (a module mid-edit keeps its
|
|
165
|
-
* last-known projection)
|
|
166
|
-
*
|
|
167
|
-
*
|
|
248
|
+
* last-known projection), and all refreshed projections keep their
|
|
249
|
+
* last-known state when any registration fails to load; the strict
|
|
250
|
+
* discoverClientUrlProjections() pass still surfaces real failures afterwards.
|
|
251
|
+
* No-ops on cold start (nothing recorded yet) and when the SSR runner is
|
|
252
|
+
* unavailable.
|
|
168
253
|
*/
|
|
169
254
|
export async function refreshRecordedClientUrlProjections(
|
|
170
255
|
state: DiscoveryState,
|
|
171
256
|
ssrEnv: ClientUrlSsrEnvironment | undefined,
|
|
172
257
|
serverMod: ClientUrlProjectionServerModule,
|
|
258
|
+
rscEnv: ClientUrlRscEnvironment,
|
|
173
259
|
): Promise<void> {
|
|
174
260
|
const sourceByReferenceId = state.clientUrlSourceByReferenceId;
|
|
175
261
|
if (
|
|
@@ -189,6 +275,7 @@ export async function refreshRecordedClientUrlProjections(
|
|
|
189
275
|
}
|
|
190
276
|
}
|
|
191
277
|
|
|
278
|
+
const refreshed = new Map<string, ClientUrlProjection>();
|
|
192
279
|
for (const [source, referenceIds] of referenceIdsBySource) {
|
|
193
280
|
let module: Record<string, unknown>;
|
|
194
281
|
try {
|
|
@@ -209,15 +296,29 @@ export async function refreshRecordedClientUrlProjections(
|
|
|
209
296
|
projection.routes.map((route) => route.pattern).join(", "),
|
|
210
297
|
);
|
|
211
298
|
for (const referenceId of referenceIds) {
|
|
212
|
-
|
|
213
|
-
// The eager routes-manifest virtual module REPLAYS this state map
|
|
214
|
-
// (clearClientUrlProjections + set) whenever it re-evaluates — and the
|
|
215
|
-
// watcher's gen-file write invalidates it right before the entry import,
|
|
216
|
-
// so a stale map here would clobber the registry install above before
|
|
217
|
-
// the router materializes its mount. Keep both in sync.
|
|
218
|
-
state.clientUrlProjectionMap?.set(referenceId, projection);
|
|
299
|
+
refreshed.set(referenceId, projection);
|
|
219
300
|
}
|
|
220
301
|
}
|
|
302
|
+
if (refreshed.size === 0) return;
|
|
303
|
+
// The eager routes-manifest virtual module REPLAYS the state map
|
|
304
|
+
// (clearClientUrlProjections + set) whenever it re-evaluates — and the
|
|
305
|
+
// watcher's gen-file write invalidates it right before the entry import, so
|
|
306
|
+
// a stale map would clobber the registry install before the router
|
|
307
|
+
// materializes its mount: the install keeps both in sync.
|
|
308
|
+
try {
|
|
309
|
+
await installClientUrlProjections(
|
|
310
|
+
state,
|
|
311
|
+
rscEnv,
|
|
312
|
+
serverMod,
|
|
313
|
+
refreshed,
|
|
314
|
+
false,
|
|
315
|
+
);
|
|
316
|
+
} catch (error) {
|
|
317
|
+
debug?.(
|
|
318
|
+
"pre-entry refresh: predicate registrations failed to load: %s",
|
|
319
|
+
error instanceof Error ? error.message : String(error),
|
|
320
|
+
);
|
|
321
|
+
}
|
|
221
322
|
}
|
|
222
323
|
|
|
223
324
|
/**
|
|
@@ -235,12 +336,18 @@ export async function discoverClientUrlProjections(
|
|
|
235
336
|
state: DiscoveryState,
|
|
236
337
|
ssrEnv: ClientUrlSsrEnvironment | undefined,
|
|
237
338
|
serverMod: ClientUrlProjectionServerModule,
|
|
339
|
+
rscEnv: ClientUrlRscEnvironment,
|
|
238
340
|
): Promise<void> {
|
|
239
341
|
scanForClientUrlModules(state);
|
|
240
342
|
const sourceByReferenceId = state.clientUrlSourceByReferenceId;
|
|
241
343
|
if (!sourceByReferenceId?.size) {
|
|
242
|
-
|
|
243
|
-
|
|
344
|
+
await installClientUrlProjections(
|
|
345
|
+
state,
|
|
346
|
+
rscEnv,
|
|
347
|
+
serverMod,
|
|
348
|
+
new Map(),
|
|
349
|
+
true,
|
|
350
|
+
);
|
|
244
351
|
return;
|
|
245
352
|
}
|
|
246
353
|
|
|
@@ -313,9 +420,91 @@ export async function discoverClientUrlProjections(
|
|
|
313
420
|
}
|
|
314
421
|
}
|
|
315
422
|
|
|
316
|
-
|
|
317
|
-
|
|
423
|
+
await installClientUrlProjections(
|
|
424
|
+
state,
|
|
425
|
+
rscEnv,
|
|
426
|
+
serverMod,
|
|
427
|
+
nextProjections,
|
|
428
|
+
true,
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Install `projections` in the realm of `serverMod` and in `state` (all of
|
|
434
|
+
* them with `replace`, else over the installed ones), once `rscEnv` has
|
|
435
|
+
* loaded the registrations of every revalidate() key they read
|
|
436
|
+
* (assignClientUrlRevalidateFiles). Node dev serves requests in that realm,
|
|
437
|
+
* and a request materializes the installed projection, so a key whose
|
|
438
|
+
* registration has not loaded must never be installed.
|
|
439
|
+
*/
|
|
440
|
+
async function installClientUrlProjections(
|
|
441
|
+
state: DiscoveryState,
|
|
442
|
+
rscEnv: ClientUrlRscEnvironment,
|
|
443
|
+
serverMod: ClientUrlProjectionServerModule,
|
|
444
|
+
projections: ReadonlyMap<string, ClientUrlProjection>,
|
|
445
|
+
replace: boolean,
|
|
446
|
+
): Promise<void> {
|
|
447
|
+
const next = new Map(replace ? undefined : state.clientUrlProjectionMap);
|
|
448
|
+
for (const [id, projection] of projections) next.set(id, projection);
|
|
449
|
+
await assignClientUrlRevalidateFiles(state, rscEnv, next);
|
|
450
|
+
if (replace) serverMod.clearClientUrlProjections();
|
|
451
|
+
for (const [id, projection] of projections) {
|
|
318
452
|
serverMod.setClientUrlProjection(id, projection);
|
|
319
453
|
}
|
|
320
|
-
state.clientUrlProjectionMap =
|
|
454
|
+
state.clientUrlProjectionMap = next;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Assign each group of `projections` the other files holding the predicates
|
|
459
|
+
* it reads (each key's file, as the hoist transform recorded it: a key with
|
|
460
|
+
* none stays unregistered, and materialization names it), after loading their
|
|
461
|
+
* registrations into `rscEnv`. The group's own registrations load with its
|
|
462
|
+
* proxy, and its `?rango-revalidate` module imports the assigned files'
|
|
463
|
+
* (plugins/client-urls-revalidate-hoist.ts). When a group's files change, that
|
|
464
|
+
* module is invalidated, so a later evaluation (a reload, a request realm)
|
|
465
|
+
* imports the current ones. Another realm (workerd, a production bundle)
|
|
466
|
+
* loads a group only after discovery: its entry imports the routes-manifest
|
|
467
|
+
* module first, whose load waits for discovery (RSC_ENTRY_BOOTSTRAP_IMPORTS),
|
|
468
|
+
* and a build discovers in buildStart.
|
|
469
|
+
*/
|
|
470
|
+
export async function assignClientUrlRevalidateFiles(
|
|
471
|
+
state: Pick<
|
|
472
|
+
DiscoveryState,
|
|
473
|
+
"clientUrlRevalidate" | "clientUrlSourceByReferenceId"
|
|
474
|
+
>,
|
|
475
|
+
rscEnv: ClientUrlRscEnvironment,
|
|
476
|
+
projections: ReadonlyMap<string, ClientUrlProjection>,
|
|
477
|
+
): Promise<void> {
|
|
478
|
+
const { fileByKey, filesByGroup } = state.clientUrlRevalidate;
|
|
479
|
+
const next = new Map<string, readonly string[]>();
|
|
480
|
+
for (const [referenceId, projection] of projections) {
|
|
481
|
+
const group = state.clientUrlSourceByReferenceId?.get(referenceId);
|
|
482
|
+
if (group === undefined || next.has(group)) continue;
|
|
483
|
+
const files = new Set<string>();
|
|
484
|
+
for (const owner of [
|
|
485
|
+
...projection.routes,
|
|
486
|
+
...(projection.intercepts ?? []),
|
|
487
|
+
]) {
|
|
488
|
+
for (const key of owner.loaderRevalidate?.flat() ?? []) {
|
|
489
|
+
const file = typeof key === "string" ? fileByKey.get(key) : undefined;
|
|
490
|
+
if (file !== undefined && file !== group) files.add(file);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
next.set(group, [...files].sort());
|
|
494
|
+
}
|
|
495
|
+
await Promise.all(
|
|
496
|
+
[...new Set([...next.values()].flat())].map((file) =>
|
|
497
|
+
rscEnv.runner.import(hoistedRevalidateId(file)),
|
|
498
|
+
),
|
|
499
|
+
);
|
|
500
|
+
const graph = rscEnv.moduleGraph;
|
|
501
|
+
for (const [group, files] of next) {
|
|
502
|
+
if ((filesByGroup.get(group) ?? []).join("\0") === files.join("\0")) {
|
|
503
|
+
continue;
|
|
504
|
+
}
|
|
505
|
+
const mod = graph?.getModuleById(hoistedRevalidateId(group));
|
|
506
|
+
if (graph && mod) graph.invalidateModule(mod);
|
|
507
|
+
}
|
|
508
|
+
filesByGroup.clear();
|
|
509
|
+
for (const [group, files] of next) filesByGroup.set(group, files);
|
|
321
510
|
}
|
|
@@ -138,7 +138,7 @@ async function discoverRoutersPass(
|
|
|
138
138
|
serverMod: any,
|
|
139
139
|
): Promise<any> {
|
|
140
140
|
await timed(debug, "inner: refresh recorded client URL projections", () =>
|
|
141
|
-
refreshRecordedClientUrlProjections(state, ssrEnv, serverMod),
|
|
141
|
+
refreshRecordedClientUrlProjections(state, ssrEnv, serverMod, rscEnv),
|
|
142
142
|
);
|
|
143
143
|
|
|
144
144
|
// Import the entry file via RSC environment.
|
|
@@ -194,7 +194,7 @@ async function discoverRoutersPass(
|
|
|
194
194
|
}
|
|
195
195
|
|
|
196
196
|
await timed(debug, "inner: discover client URL projections", () =>
|
|
197
|
-
discoverClientUrlProjections(state, ssrEnv, serverMod),
|
|
197
|
+
discoverClientUrlProjections(state, ssrEnv, serverMod, rscEnv),
|
|
198
198
|
);
|
|
199
199
|
|
|
200
200
|
// generateManifestFull must run in the RSC runner realm: it invokes the
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import { contextSet, hasContextVars } from "../../context-var.js";
|
|
10
10
|
import { sha256 } from "./build-versions.js";
|
|
11
11
|
import {
|
|
12
|
+
createGetParamsReverse,
|
|
12
13
|
encodePathParam,
|
|
13
14
|
substituteRouteParams,
|
|
14
15
|
runWithConcurrency,
|
|
@@ -76,12 +77,7 @@ export async function expandPrerenderRoutes(
|
|
|
76
77
|
for (const { manifest: m } of allManifests) {
|
|
77
78
|
if (m.routeManifest) Object.assign(allRoutes, m.routeManifest);
|
|
78
79
|
}
|
|
79
|
-
const getParamsReverse = (
|
|
80
|
-
const pattern = allRoutes[name];
|
|
81
|
-
if (!pattern) throw new Error(`Unknown route: "${name}"`);
|
|
82
|
-
if (!params) return pattern;
|
|
83
|
-
return substituteRouteParams(pattern, params);
|
|
84
|
-
};
|
|
80
|
+
const getParamsReverse = createGetParamsReverse(allRoutes);
|
|
85
81
|
|
|
86
82
|
let resolvedRoutes = 0;
|
|
87
83
|
let totalDynamic = 0;
|
|
@@ -52,6 +52,29 @@ export interface PluginOptions {
|
|
|
52
52
|
* budget into its capture/render handlers.
|
|
53
53
|
*/
|
|
54
54
|
progressiveChunkSize?: number;
|
|
55
|
+
/**
|
|
56
|
+
* The index rango()'s clientUrls() revalidate() hoist plugin shares, so
|
|
57
|
+
* discovery's temp server records keys into it and assigns the files the
|
|
58
|
+
* dev server's or build's group modules import.
|
|
59
|
+
*/
|
|
60
|
+
clientUrlRevalidate?: ClientUrlRevalidateIndex;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* What the clientUrls() revalidate() hoist
|
|
65
|
+
* (plugins/client-urls-revalidate-hoist.ts) shares with discovery across the
|
|
66
|
+
* plugin instances of one rango(), the dev server or build and discovery's
|
|
67
|
+
* temp server: the file of every key the hoist transform saw, in any
|
|
68
|
+
* environment, and each group's files, which discovery assigns from the
|
|
69
|
+
* group's projection and the group's `?rango-revalidate` module imports.
|
|
70
|
+
*/
|
|
71
|
+
export interface ClientUrlRevalidateIndex {
|
|
72
|
+
readonly fileByKey: Map<string, string>;
|
|
73
|
+
readonly filesByGroup: Map<string, readonly string[]>;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function createClientUrlRevalidateIndex(): ClientUrlRevalidateIndex {
|
|
77
|
+
return { fileByKey: new Map(), filesByGroup: new Map() };
|
|
55
78
|
}
|
|
56
79
|
|
|
57
80
|
export interface PrecomputedEntry {
|
|
@@ -129,6 +152,7 @@ export interface DiscoveryState {
|
|
|
129
152
|
perRouterManifestDataMap: Map<string, Record<string, string>>;
|
|
130
153
|
clientUrlSourceByReferenceId?: Map<string, string>;
|
|
131
154
|
clientUrlProjectionMap?: Map<string, ClientUrlProjection>;
|
|
155
|
+
clientUrlRevalidate: ClientUrlRevalidateIndex;
|
|
132
156
|
|
|
133
157
|
prerenderManifestEntries: Record<string, string> | null;
|
|
134
158
|
staticManifestEntries: Record<string, string> | null;
|
|
@@ -251,6 +275,8 @@ export function createDiscoveryState(
|
|
|
251
275
|
perRouterManifestDataMap: new Map(),
|
|
252
276
|
clientUrlSourceByReferenceId: new Map(),
|
|
253
277
|
clientUrlProjectionMap: new Map(),
|
|
278
|
+
clientUrlRevalidate:
|
|
279
|
+
opts?.clientUrlRevalidate ?? createClientUrlRevalidateIndex(),
|
|
254
280
|
|
|
255
281
|
prerenderManifestEntries: null,
|
|
256
282
|
staticManifestEntries: null,
|