@rebask/core 0.1.5 → 0.2.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/entry.rsc.d.ts +1 -0
- package/dist/entry.rsc.d.ts.map +1 -1
- package/dist/entry.rsc.js +1 -0
- package/dist/handlers/markdown.d.ts +16 -0
- package/dist/handlers/markdown.d.ts.map +1 -0
- package/dist/handlers/markdown.js +161 -0
- package/dist/handlers/rsc/cache-lookup.d.ts.map +1 -1
- package/dist/handlers/rsc/cache-lookup.js +10 -4
- package/dist/handlers/rsc/index.js +1 -1
- package/dist/handlers/rsc/render-to-response.js +6 -2
- package/dist/handlers/rsc/segment-shell.d.ts +1 -1
- package/dist/handlers/rsc/segment-shell.d.ts.map +1 -1
- package/dist/handlers/rsc/segment-shell.js +5 -9
- package/dist/handlers/rsc/state.d.ts +1 -0
- package/dist/handlers/rsc/state.d.ts.map +1 -1
- package/dist/handlers/rsc/state.js +4 -0
- package/dist/lib/app-manifest.d.ts +12 -4
- package/dist/lib/app-manifest.d.ts.map +1 -1
- package/dist/lib/app-manifest.js +5 -0
- package/dist/lib/cache-key-tags.d.ts +2 -0
- package/dist/lib/cache-key-tags.d.ts.map +1 -1
- package/dist/lib/cache-key-tags.js +2 -0
- package/dist/lib/cache-storage.d.ts +7 -2
- package/dist/lib/cache-storage.d.ts.map +1 -1
- package/dist/lib/cache-storage.js +1 -0
- package/dist/lib/config.d.ts +2 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +8 -3
- package/dist/lib/dev-page-exports.d.ts +5 -0
- package/dist/lib/dev-page-exports.d.ts.map +1 -0
- package/dist/lib/dev-page-exports.js +14 -0
- package/dist/lib/lru-cache.d.ts.map +1 -1
- package/dist/lib/lru-cache.js +2 -0
- package/dist/lib/markdown.d.ts +5 -0
- package/dist/lib/markdown.d.ts.map +1 -0
- package/dist/lib/markdown.js +53 -0
- package/dist/lib/page-exports.d.ts +9 -0
- package/dist/lib/page-exports.d.ts.map +1 -0
- package/dist/lib/page-exports.js +47 -0
- package/dist/lib/ppr/cache-status.d.ts +3 -0
- package/dist/lib/ppr/cache-status.d.ts.map +1 -0
- package/dist/lib/ppr/cache-status.js +5 -0
- package/dist/lib/ppr/headers.d.ts +1 -1
- package/dist/lib/ppr/headers.d.ts.map +1 -1
- package/dist/lib/ppr/headers.js +1 -1
- package/dist/lib/prerender-params.d.ts +1 -1
- package/dist/lib/prerender-params.d.ts.map +1 -1
- package/dist/lib/prerender-params.js +17 -2
- package/dist/lib/project-files.d.ts +2 -1
- package/dist/lib/project-files.d.ts.map +1 -1
- package/dist/lib/project-files.js +16 -6
- package/dist/lib/route-builder.d.ts +4 -3
- package/dist/lib/route-builder.d.ts.map +1 -1
- package/dist/lib/route-builder.js +61 -13
- package/dist/lib/route-matcher.d.ts +4 -0
- package/dist/lib/route-matcher.d.ts.map +1 -1
- package/dist/lib/rsc-request.d.ts.map +1 -1
- package/dist/lib/rsc-request.js +3 -3
- package/dist/lib/server-router/get-router.d.ts.map +1 -1
- package/dist/lib/server-router/get-router.js +71 -16
- package/dist/lib/server-router/types.d.ts +4 -1
- package/dist/lib/server-router/types.d.ts.map +1 -1
- package/dist/lib/write-behind-cache.d.ts.map +1 -1
- package/dist/lib/write-behind-cache.js +5 -3
- package/dist/plugins/devtools.d.ts.map +1 -1
- package/dist/plugins/devtools.js +2 -2
- package/dist/plugins/ppr.d.ts +5 -0
- package/dist/plugins/ppr.d.ts.map +1 -1
- package/dist/plugins/prerender.d.ts.map +1 -1
- package/dist/plugins/prerender.js +114 -70
- package/dist/plugins/router/wint-router.d.ts.map +1 -1
- package/dist/plugins/router/wint-router.js +4 -2
- package/dist/router.d.ts +1 -1
- package/dist/router.js +1 -1
- package/dist/server-router.d.ts +1 -1
- package/dist/server-router.d.ts.map +1 -1
- package/dist/vite.d.ts +20 -1
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +17 -4
- package/package.json +1 -5
- package/dist/components/prefetch-links.d.ts +0 -5
- package/dist/components/prefetch-links.d.ts.map +0 -1
- package/dist/components/prefetch-links.js +0 -174
package/dist/vite.js
CHANGED
|
@@ -78,7 +78,7 @@ async function resolveRuntimeConfigPath(root, configPath) {
|
|
|
78
78
|
return (await findProjectFile(root, "rebask.config")) || "";
|
|
79
79
|
}
|
|
80
80
|
export function rebask(options = {}) {
|
|
81
|
-
const { clientTraceMetadata = [], configPath, dedupeFetch = true, deploymentId, fullHtmlForBots = false, proxyTimeout = 30_000, shellPrerenderTimeout = 30_000, } = options;
|
|
81
|
+
const { clientTraceMetadata = [], configPath, dedupeFetch = true, deploymentId, fullHtmlForBots = false, markdownUrls = true, proxyTimeout = 30_000, shellPrerenderTimeout = 30_000, } = options;
|
|
82
82
|
const prerender = options.prerender ?? true;
|
|
83
83
|
let resolvedConfigPath = null;
|
|
84
84
|
let resolvedInstrumentationPath = null;
|
|
@@ -108,6 +108,7 @@ export function rebask(options = {}) {
|
|
|
108
108
|
})),
|
|
109
109
|
getLayoutRoutes: () => layoutRoutes,
|
|
110
110
|
getRouteHandlerRoutes: () => routeHandlerRoutes,
|
|
111
|
+
hasMarkdownUrls: () => markdownUrls,
|
|
111
112
|
getRscEntry: () => rscEntry,
|
|
112
113
|
setDeploymentId: (deploymentId) => {
|
|
113
114
|
adapterDeploymentId = deploymentId ?? null;
|
|
@@ -182,6 +183,7 @@ export function rebask(options = {}) {
|
|
|
182
183
|
return {
|
|
183
184
|
define: {
|
|
184
185
|
__REBASK_FULL_HTML_FOR_BOTS: JSON.stringify(fullHtmlForBots),
|
|
186
|
+
__REBASK_MARKDOWN_URLS: JSON.stringify(markdownUrls),
|
|
185
187
|
__REBASK_DEDUPE_FETCH: JSON.stringify(dedupeFetch),
|
|
186
188
|
__REBASK_CLIENT_TRACE_HEADERS__: JSON.stringify(clientTraceMetadata),
|
|
187
189
|
__REBASK_PROXY_TIMEOUT: proxyTimeout === null ? "undefined" : JSON.stringify(proxyTimeout),
|
|
@@ -334,6 +336,17 @@ export function rebask(options = {}) {
|
|
|
334
336
|
const isAppFile = relativeFile.startsWith("/app/");
|
|
335
337
|
const isPageFile = isAppFile && PAGE_EXTENSIONS.some((ext) => relativeFile.endsWith(`/page.${ext}`));
|
|
336
338
|
const isLayoutFile = isAppFile && PAGE_EXTENSIONS.some((ext) => relativeFile.endsWith(`/layout.${ext}`));
|
|
339
|
+
const isMarkdownFile = isAppFile &&
|
|
340
|
+
(relativeFile.endsWith("/markdown.md") ||
|
|
341
|
+
PAGE_EXTENSIONS.some((ext) => relativeFile.endsWith(`/markdown.${ext}`)));
|
|
342
|
+
if (isMarkdownFile) {
|
|
343
|
+
const moduleGraph = this.environment.moduleGraph;
|
|
344
|
+
for (const mod of moduleGraph.getModulesByFile(file) ?? []) {
|
|
345
|
+
moduleGraph.invalidateModule(mod);
|
|
346
|
+
}
|
|
347
|
+
notifyRscUpdate(server, "all");
|
|
348
|
+
return [];
|
|
349
|
+
}
|
|
337
350
|
if (isPageFile || isLayoutFile) {
|
|
338
351
|
const scope = type === "update" && isPageFile ? resolveServerUpdateScope(modules, projectRoot) : "all";
|
|
339
352
|
notifyRscUpdate(server, scope);
|
|
@@ -363,13 +376,13 @@ export function rebask(options = {}) {
|
|
|
363
376
|
throw error;
|
|
364
377
|
}
|
|
365
378
|
});
|
|
366
|
-
const { pages, layouts, routeHandlers } = await scanAppDirectory(appDir);
|
|
367
|
-
assertNoPageRouteHandlerConflicts(pages, routeHandlers);
|
|
379
|
+
const { pages, layouts, routeHandlers, markdownFiles } = await scanAppDirectory(appDir, markdownUrls);
|
|
380
|
+
assertNoPageRouteHandlerConflicts(pages, routeHandlers, markdownFiles);
|
|
368
381
|
routeHandlerRoutes = buildRouteHandlerRoutes(routeHandlers);
|
|
369
382
|
// Generate page/layout routes only for the RSC environment
|
|
370
383
|
if (this.environment.name === "rsc") {
|
|
371
384
|
// Store routes in shared state (accessible via plugin API)
|
|
372
|
-
routes = buildRoutes(layouts, pages);
|
|
385
|
+
routes = buildRoutes(layouts, pages, markdownFiles);
|
|
373
386
|
layoutRoutes = buildLayoutRoutes(layouts);
|
|
374
387
|
}
|
|
375
388
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rebask/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Vite plugin and runtime for React Server Components",
|
|
5
5
|
"homepage": "https://github.com/lfades/vext#readme",
|
|
6
6
|
"license": "MIT",
|
|
@@ -75,10 +75,6 @@
|
|
|
75
75
|
"types": "./dist/cookies.d.ts",
|
|
76
76
|
"default": "./dist/cookies.js"
|
|
77
77
|
},
|
|
78
|
-
"./components/prefetch-links": {
|
|
79
|
-
"types": "./dist/components/prefetch-links.d.ts",
|
|
80
|
-
"default": "./dist/components/prefetch-links.js"
|
|
81
|
-
},
|
|
82
78
|
"./components/link": {
|
|
83
79
|
"types": "./dist/components/link.d.ts",
|
|
84
80
|
"default": "./dist/components/link.js"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"prefetch-links.d.ts","sourceRoot":"","sources":["../../src/components/prefetch-links.tsx"],"names":[],"mappings":"AAIA,OAAO,EAKN,KAAK,aAAa,EAClB,MAAM,0BAA0B,CAAA;AAIjC,MAAM,WAAW,kBAAmB,SAAQ,aAAa;CAAG;AAI5D,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,EAAE,kBAAkB,QA4H7D"}
|
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import { useAtom } from "@lfades/atom";
|
|
3
|
-
import { useEffect } from "react";
|
|
4
|
-
import { getNavigationLink, getPrefetchStrategy, isLinkVisible, isPrefetchable, } from "../lib/prefetch-utils.js";
|
|
5
|
-
import { useOnCacheInvalidation, useRouter } from "../router.js";
|
|
6
|
-
import { useRouterCache } from "./cache-slot.js";
|
|
7
|
-
export function PrefetchLinks({ prefetch }) {
|
|
8
|
-
const router = useRouter();
|
|
9
|
-
// Navigation effects must not suspend link discovery during shell rendering.
|
|
10
|
-
const [pathname] = useAtom(useRouterCache().pathnameAtom);
|
|
11
|
-
const prefetchVisible = () => {
|
|
12
|
-
if (!import.meta.env.PROD)
|
|
13
|
-
return;
|
|
14
|
-
const strategy = getPrefetchStrategy(prefetch);
|
|
15
|
-
document.querySelectorAll("a").forEach((el) => {
|
|
16
|
-
// Offscreen links prefetch when they scroll back in or remount.
|
|
17
|
-
if (!(el instanceof HTMLAnchorElement) || !isPrefetchable(el) || !isLinkVisible(el)) {
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
if (strategy === "viewport") {
|
|
21
|
-
router.prefetch(el.href, { skipShell: true }).catch(() => { });
|
|
22
|
-
}
|
|
23
|
-
else {
|
|
24
|
-
router.prefetchStatic(el.href).catch(() => { });
|
|
25
|
-
}
|
|
26
|
-
});
|
|
27
|
-
};
|
|
28
|
-
useEffect(() => {
|
|
29
|
-
function onClick(e) {
|
|
30
|
-
const link = getNavigationLink(e);
|
|
31
|
-
if (link) {
|
|
32
|
-
e.preventDefault();
|
|
33
|
-
const options = { scroll: link.dataset.scroll !== "false" };
|
|
34
|
-
if (link.href === window.location.href)
|
|
35
|
-
router.replace(link.href, options);
|
|
36
|
-
else
|
|
37
|
-
router.push(link.href, options);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
document.addEventListener("click", onClick);
|
|
41
|
-
return () => {
|
|
42
|
-
document.removeEventListener("click", onClick);
|
|
43
|
-
};
|
|
44
|
-
}, [router]);
|
|
45
|
-
useEffect(() => {
|
|
46
|
-
if (!import.meta.env.PROD)
|
|
47
|
-
return;
|
|
48
|
-
const strategy = getPrefetchStrategy(prefetch);
|
|
49
|
-
const onPrefetchStatic = (el) => {
|
|
50
|
-
if (el instanceof HTMLAnchorElement && isPrefetchable(el)) {
|
|
51
|
-
router.prefetchStatic(el.href).catch(() => { });
|
|
52
|
-
}
|
|
53
|
-
};
|
|
54
|
-
const onPrefetchFull = (el) => {
|
|
55
|
-
if (el instanceof HTMLAnchorElement && isPrefetchable(el)) {
|
|
56
|
-
router.prefetch(el.href).catch(() => { });
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
if (strategy === "viewport") {
|
|
60
|
-
// A viewport full prefetch goes for the full RSC directly, never a shell.
|
|
61
|
-
return listenViewport((el) => {
|
|
62
|
-
if (el instanceof HTMLAnchorElement && isPrefetchable(el)) {
|
|
63
|
-
router.prefetch(el.href, { skipShell: true }).catch(() => { });
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
if (strategy === "intent") {
|
|
68
|
-
// A link's static output (a full static page, or a dynamic route's shell) is always
|
|
69
|
-
// prefetched once it is in view.
|
|
70
|
-
const offViewport = listenViewport(onPrefetchStatic);
|
|
71
|
-
const offIntent = listenIntent(onPrefetchFull);
|
|
72
|
-
return () => {
|
|
73
|
-
offViewport();
|
|
74
|
-
offIntent();
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
return listenViewport(onPrefetchStatic);
|
|
78
|
-
}, [router, prefetch]);
|
|
79
|
-
// Prefetch visible links again after an invalidation
|
|
80
|
-
useOnCacheInvalidation(prefetchVisible);
|
|
81
|
-
// The persistent observer only fires on intersection changes, so links that stay in view across
|
|
82
|
-
// a navigation keep a dropped shell otherwise.
|
|
83
|
-
useEffect(prefetchVisible, [router, prefetch, pathname]);
|
|
84
|
-
// Always-on stale check on hover/focus/touch, independent of the prefetch strategy. Lets links
|
|
85
|
-
// still catch a stale segment before navigation.
|
|
86
|
-
useEffect(() => {
|
|
87
|
-
function check(target) {
|
|
88
|
-
if (!(target instanceof Element))
|
|
89
|
-
return;
|
|
90
|
-
const link = target.closest("a");
|
|
91
|
-
if (link instanceof HTMLAnchorElement && isPrefetchable(link)) {
|
|
92
|
-
router.revalidateIfStale(link.href);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
function onMouseEnter(e) {
|
|
96
|
-
check(e.target);
|
|
97
|
-
}
|
|
98
|
-
function onFocus(e) {
|
|
99
|
-
check(e.target);
|
|
100
|
-
}
|
|
101
|
-
function onTouchStart(e) {
|
|
102
|
-
check(e.target);
|
|
103
|
-
}
|
|
104
|
-
// Capture phase so we see the events on every <a>, not just bubbling.
|
|
105
|
-
document.addEventListener("mouseenter", onMouseEnter, true);
|
|
106
|
-
document.addEventListener("focusin", onFocus, true);
|
|
107
|
-
document.addEventListener("touchstart", onTouchStart, { passive: true });
|
|
108
|
-
return () => {
|
|
109
|
-
document.removeEventListener("mouseenter", onMouseEnter, true);
|
|
110
|
-
document.removeEventListener("focusin", onFocus, true);
|
|
111
|
-
document.removeEventListener("touchstart", onTouchStart);
|
|
112
|
-
};
|
|
113
|
-
}, [router]);
|
|
114
|
-
return null;
|
|
115
|
-
}
|
|
116
|
-
function listenViewport(onPrefetch) {
|
|
117
|
-
const observer = new IntersectionObserver((entries) => {
|
|
118
|
-
for (const entry of entries) {
|
|
119
|
-
if (entry.isIntersecting) {
|
|
120
|
-
// Stays observed: dropped shells are prefetched again the next time the link intersects.
|
|
121
|
-
onPrefetch(entry.target);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}, { rootMargin: "200px" });
|
|
125
|
-
const observeLink = (link) => {
|
|
126
|
-
observer.observe(link);
|
|
127
|
-
};
|
|
128
|
-
document.querySelectorAll("a").forEach(observeLink);
|
|
129
|
-
const mutationObserver = new MutationObserver((mutations) => {
|
|
130
|
-
for (const mutation of mutations) {
|
|
131
|
-
if (mutation.type === "childList") {
|
|
132
|
-
mutation.addedNodes.forEach((node) => {
|
|
133
|
-
if (node instanceof HTMLAnchorElement) {
|
|
134
|
-
observeLink(node);
|
|
135
|
-
}
|
|
136
|
-
else if (node instanceof Element) {
|
|
137
|
-
node.querySelectorAll("a").forEach(observeLink);
|
|
138
|
-
}
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
if (mutation.type === "attributes" &&
|
|
142
|
-
mutation.attributeName === "href" &&
|
|
143
|
-
mutation.target instanceof HTMLAnchorElement) {
|
|
144
|
-
observer.unobserve(mutation.target);
|
|
145
|
-
observeLink(mutation.target);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
});
|
|
149
|
-
mutationObserver.observe(document.body, {
|
|
150
|
-
childList: true,
|
|
151
|
-
subtree: true,
|
|
152
|
-
attributes: true,
|
|
153
|
-
attributeFilter: ["href"],
|
|
154
|
-
});
|
|
155
|
-
return () => {
|
|
156
|
-
observer.disconnect();
|
|
157
|
-
mutationObserver.disconnect();
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
function listenIntent(onPrefetch) {
|
|
161
|
-
function onMouseEnter(e) {
|
|
162
|
-
onPrefetch(e.target instanceof Element ? e.target.closest("a") : null);
|
|
163
|
-
}
|
|
164
|
-
function onFocus(e) {
|
|
165
|
-
onPrefetch(e.target);
|
|
166
|
-
}
|
|
167
|
-
// Use capture phase to catch events before they bubble
|
|
168
|
-
document.addEventListener("mouseenter", onMouseEnter, true);
|
|
169
|
-
document.addEventListener("focusin", onFocus, true);
|
|
170
|
-
return () => {
|
|
171
|
-
document.removeEventListener("mouseenter", onMouseEnter, true);
|
|
172
|
-
document.removeEventListener("focusin", onFocus, true);
|
|
173
|
-
};
|
|
174
|
-
}
|