@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.
Files changed (83) hide show
  1. package/dist/entry.rsc.d.ts +1 -0
  2. package/dist/entry.rsc.d.ts.map +1 -1
  3. package/dist/entry.rsc.js +1 -0
  4. package/dist/handlers/markdown.d.ts +16 -0
  5. package/dist/handlers/markdown.d.ts.map +1 -0
  6. package/dist/handlers/markdown.js +161 -0
  7. package/dist/handlers/rsc/cache-lookup.d.ts.map +1 -1
  8. package/dist/handlers/rsc/cache-lookup.js +10 -4
  9. package/dist/handlers/rsc/index.js +1 -1
  10. package/dist/handlers/rsc/render-to-response.js +6 -2
  11. package/dist/handlers/rsc/segment-shell.d.ts +1 -1
  12. package/dist/handlers/rsc/segment-shell.d.ts.map +1 -1
  13. package/dist/handlers/rsc/segment-shell.js +5 -9
  14. package/dist/handlers/rsc/state.d.ts +1 -0
  15. package/dist/handlers/rsc/state.d.ts.map +1 -1
  16. package/dist/handlers/rsc/state.js +4 -0
  17. package/dist/lib/app-manifest.d.ts +12 -4
  18. package/dist/lib/app-manifest.d.ts.map +1 -1
  19. package/dist/lib/app-manifest.js +5 -0
  20. package/dist/lib/cache-key-tags.d.ts +2 -0
  21. package/dist/lib/cache-key-tags.d.ts.map +1 -1
  22. package/dist/lib/cache-key-tags.js +2 -0
  23. package/dist/lib/cache-storage.d.ts +7 -2
  24. package/dist/lib/cache-storage.d.ts.map +1 -1
  25. package/dist/lib/cache-storage.js +1 -0
  26. package/dist/lib/config.d.ts +2 -0
  27. package/dist/lib/config.d.ts.map +1 -1
  28. package/dist/lib/config.js +8 -3
  29. package/dist/lib/dev-page-exports.d.ts +5 -0
  30. package/dist/lib/dev-page-exports.d.ts.map +1 -0
  31. package/dist/lib/dev-page-exports.js +14 -0
  32. package/dist/lib/lru-cache.d.ts.map +1 -1
  33. package/dist/lib/lru-cache.js +2 -0
  34. package/dist/lib/markdown.d.ts +5 -0
  35. package/dist/lib/markdown.d.ts.map +1 -0
  36. package/dist/lib/markdown.js +53 -0
  37. package/dist/lib/page-exports.d.ts +9 -0
  38. package/dist/lib/page-exports.d.ts.map +1 -0
  39. package/dist/lib/page-exports.js +47 -0
  40. package/dist/lib/ppr/cache-status.d.ts +3 -0
  41. package/dist/lib/ppr/cache-status.d.ts.map +1 -0
  42. package/dist/lib/ppr/cache-status.js +5 -0
  43. package/dist/lib/ppr/headers.d.ts +1 -1
  44. package/dist/lib/ppr/headers.d.ts.map +1 -1
  45. package/dist/lib/ppr/headers.js +1 -1
  46. package/dist/lib/prerender-params.d.ts +1 -1
  47. package/dist/lib/prerender-params.d.ts.map +1 -1
  48. package/dist/lib/prerender-params.js +17 -2
  49. package/dist/lib/project-files.d.ts +2 -1
  50. package/dist/lib/project-files.d.ts.map +1 -1
  51. package/dist/lib/project-files.js +16 -6
  52. package/dist/lib/route-builder.d.ts +4 -3
  53. package/dist/lib/route-builder.d.ts.map +1 -1
  54. package/dist/lib/route-builder.js +61 -13
  55. package/dist/lib/route-matcher.d.ts +4 -0
  56. package/dist/lib/route-matcher.d.ts.map +1 -1
  57. package/dist/lib/rsc-request.d.ts.map +1 -1
  58. package/dist/lib/rsc-request.js +3 -3
  59. package/dist/lib/server-router/get-router.d.ts.map +1 -1
  60. package/dist/lib/server-router/get-router.js +71 -16
  61. package/dist/lib/server-router/types.d.ts +4 -1
  62. package/dist/lib/server-router/types.d.ts.map +1 -1
  63. package/dist/lib/write-behind-cache.d.ts.map +1 -1
  64. package/dist/lib/write-behind-cache.js +5 -3
  65. package/dist/plugins/devtools.d.ts.map +1 -1
  66. package/dist/plugins/devtools.js +2 -2
  67. package/dist/plugins/ppr.d.ts +5 -0
  68. package/dist/plugins/ppr.d.ts.map +1 -1
  69. package/dist/plugins/prerender.d.ts.map +1 -1
  70. package/dist/plugins/prerender.js +114 -70
  71. package/dist/plugins/router/wint-router.d.ts.map +1 -1
  72. package/dist/plugins/router/wint-router.js +4 -2
  73. package/dist/router.d.ts +1 -1
  74. package/dist/router.js +1 -1
  75. package/dist/server-router.d.ts +1 -1
  76. package/dist/server-router.d.ts.map +1 -1
  77. package/dist/vite.d.ts +20 -1
  78. package/dist/vite.d.ts.map +1 -1
  79. package/dist/vite.js +17 -4
  80. package/package.json +1 -5
  81. package/dist/components/prefetch-links.d.ts +0 -5
  82. package/dist/components/prefetch-links.d.ts.map +0 -1
  83. 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.1.5",
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,5 +0,0 @@
1
- import { type PrefetchProps } from "../lib/prefetch-utils.js";
2
- export interface PrefetchLinksProps extends PrefetchProps {
3
- }
4
- export declare function PrefetchLinks({ prefetch }: PrefetchLinksProps): null;
5
- //# sourceMappingURL=prefetch-links.d.ts.map
@@ -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
- }