rari 0.5.16 → 0.5.18
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/client.mjs +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{runtime-client-AtnJSL0q.mjs → runtime-client-DCkfDJpL.mjs} +116 -1
- package/dist/vite.mjs +1 -1
- package/package.json +6 -6
- package/src/router/ClientRouter.tsx +199 -0
- package/src/router/index.ts +2 -0
- package/src/router/props-extractor.ts +77 -4
package/dist/client.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { C as fetchWithTimeout, S as createNavigationError, _ as LayoutErrorBoundary, a as LoadingSpinner, b as NavigationErrorOverlay, c as createHttpRuntimeClient, d as clearPropsCacheForComponent, f as extractMetadata, g as hasServerSideDataFetching, h as extractStaticParams, i as HttpRuntimeClient, l as createLoadingBoundary, m as extractServerPropsWithCache, n as DefaultLoading, o as NotFound, p as extractServerProps, r as ErrorBoundary, s as createErrorBoundary, t as DefaultError, u as clearPropsCache, v as ClientRouter, w as LayoutManager, x as NavigationErrorHandler, y as StatePreserver } from "./runtime-client-
|
|
1
|
+
import { C as fetchWithTimeout, S as createNavigationError, _ as LayoutErrorBoundary, a as LoadingSpinner, b as NavigationErrorOverlay, c as createHttpRuntimeClient, d as clearPropsCacheForComponent, f as extractMetadata, g as hasServerSideDataFetching, h as extractStaticParams, i as HttpRuntimeClient, l as createLoadingBoundary, m as extractServerPropsWithCache, n as DefaultLoading, o as NotFound, p as extractServerProps, r as ErrorBoundary, s as createErrorBoundary, t as DefaultError, u as clearPropsCache, v as ClientRouter, w as LayoutManager, x as NavigationErrorHandler, y as StatePreserver } from "./runtime-client-DCkfDJpL.mjs";
|
|
2
2
|
|
|
3
3
|
export { ClientRouter, DefaultError, DefaultLoading, ErrorBoundary, HttpRuntimeClient, LayoutErrorBoundary, LayoutManager, LoadingSpinner, NavigationErrorHandler, NavigationErrorOverlay, NotFound, StatePreserver, clearPropsCache, clearPropsCacheForComponent, createErrorBoundary, createHttpRuntimeClient, createLoadingBoundary, createNavigationError, extractMetadata, extractServerProps, extractServerPropsWithCache, extractStaticParams, fetchWithTimeout, hasServerSideDataFetching };
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { a as headers, i as rariRouter, n as defineRariOptions, o as RariResponse, r as rari, t as defineRariConfig } from "./vite-B_QMp3M4.mjs";
|
|
2
2
|
import { i as writeManifest, n as generateAppRouteManifest, r as loadManifest, t as AppRouteGenerator } from "./app-routes-DZjfJPdB.mjs";
|
|
3
|
-
import { c as createHttpRuntimeClient, d as clearPropsCacheForComponent, f as extractMetadata, g as hasServerSideDataFetching, h as extractStaticParams, i as HttpRuntimeClient, m as extractServerPropsWithCache, p as extractServerProps, u as clearPropsCache } from "./runtime-client-
|
|
3
|
+
import { c as createHttpRuntimeClient, d as clearPropsCacheForComponent, f as extractMetadata, g as hasServerSideDataFetching, h as extractStaticParams, i as HttpRuntimeClient, m as extractServerPropsWithCache, p as extractServerProps, u as clearPropsCache } from "./runtime-client-DCkfDJpL.mjs";
|
|
4
4
|
import "./server-build-ChAPR3tl.mjs";
|
|
5
5
|
|
|
6
6
|
export { AppRouteGenerator, HttpRuntimeClient, RariResponse, clearPropsCache, clearPropsCacheForComponent, createHttpRuntimeClient, defineRariConfig, defineRariOptions, extractMetadata, extractServerProps, extractServerPropsWithCache, extractStaticParams, generateAppRouteManifest, hasServerSideDataFetching, headers, loadManifest, rari, rariRouter, writeManifest };
|
|
@@ -923,6 +923,112 @@ var StatePreserver = class {
|
|
|
923
923
|
|
|
924
924
|
//#endregion
|
|
925
925
|
//#region src/router/ClientRouter.tsx
|
|
926
|
+
function updateDocumentMetadata(metadata) {
|
|
927
|
+
if (metadata.title) document.title = metadata.title;
|
|
928
|
+
const updateOrCreateMetaTag = (selector, attributes) => {
|
|
929
|
+
let element = document.querySelector(selector);
|
|
930
|
+
if (!element) {
|
|
931
|
+
element = document.createElement("meta");
|
|
932
|
+
for (const [key, value] of Object.entries(attributes)) element.setAttribute(key, value);
|
|
933
|
+
document.head.appendChild(element);
|
|
934
|
+
} else if (attributes.content) element.setAttribute("content", attributes.content);
|
|
935
|
+
};
|
|
936
|
+
if (metadata.description) updateOrCreateMetaTag("meta[name=\"description\"]", {
|
|
937
|
+
name: "description",
|
|
938
|
+
content: metadata.description
|
|
939
|
+
});
|
|
940
|
+
if (metadata.keywords && metadata.keywords.length > 0) updateOrCreateMetaTag("meta[name=\"keywords\"]", {
|
|
941
|
+
name: "keywords",
|
|
942
|
+
content: metadata.keywords.join(", ")
|
|
943
|
+
});
|
|
944
|
+
if (metadata.viewport) updateOrCreateMetaTag("meta[name=\"viewport\"]", {
|
|
945
|
+
name: "viewport",
|
|
946
|
+
content: metadata.viewport
|
|
947
|
+
});
|
|
948
|
+
if (metadata.canonical) {
|
|
949
|
+
let canonical = document.querySelector("link[rel=\"canonical\"]");
|
|
950
|
+
if (!canonical) {
|
|
951
|
+
canonical = document.createElement("link");
|
|
952
|
+
canonical.setAttribute("rel", "canonical");
|
|
953
|
+
document.head.appendChild(canonical);
|
|
954
|
+
}
|
|
955
|
+
canonical.setAttribute("href", metadata.canonical);
|
|
956
|
+
}
|
|
957
|
+
if (metadata.robots) {
|
|
958
|
+
const robotsContent = [];
|
|
959
|
+
if (metadata.robots.index !== void 0) robotsContent.push(metadata.robots.index ? "index" : "noindex");
|
|
960
|
+
if (metadata.robots.follow !== void 0) robotsContent.push(metadata.robots.follow ? "follow" : "nofollow");
|
|
961
|
+
if (metadata.robots.nocache) robotsContent.push("nocache");
|
|
962
|
+
if (robotsContent.length > 0) updateOrCreateMetaTag("meta[name=\"robots\"]", {
|
|
963
|
+
name: "robots",
|
|
964
|
+
content: robotsContent.join(", ")
|
|
965
|
+
});
|
|
966
|
+
}
|
|
967
|
+
if (metadata.openGraph) {
|
|
968
|
+
const og = metadata.openGraph;
|
|
969
|
+
if (og.title) updateOrCreateMetaTag("meta[property=\"og:title\"]", {
|
|
970
|
+
property: "og:title",
|
|
971
|
+
content: og.title
|
|
972
|
+
});
|
|
973
|
+
if (og.description) updateOrCreateMetaTag("meta[property=\"og:description\"]", {
|
|
974
|
+
property: "og:description",
|
|
975
|
+
content: og.description
|
|
976
|
+
});
|
|
977
|
+
if (og.url) updateOrCreateMetaTag("meta[property=\"og:url\"]", {
|
|
978
|
+
property: "og:url",
|
|
979
|
+
content: og.url
|
|
980
|
+
});
|
|
981
|
+
if (og.siteName) updateOrCreateMetaTag("meta[property=\"og:site_name\"]", {
|
|
982
|
+
property: "og:site_name",
|
|
983
|
+
content: og.siteName
|
|
984
|
+
});
|
|
985
|
+
if (og.type) updateOrCreateMetaTag("meta[property=\"og:type\"]", {
|
|
986
|
+
property: "og:type",
|
|
987
|
+
content: og.type
|
|
988
|
+
});
|
|
989
|
+
if (og.images && og.images.length > 0) {
|
|
990
|
+
document.querySelectorAll("meta[property=\"og:image\"]").forEach((el) => el.remove());
|
|
991
|
+
for (const image of og.images) {
|
|
992
|
+
const meta = document.createElement("meta");
|
|
993
|
+
meta.setAttribute("property", "og:image");
|
|
994
|
+
meta.setAttribute("content", image);
|
|
995
|
+
document.head.appendChild(meta);
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
if (metadata.twitter) {
|
|
1000
|
+
const twitter = metadata.twitter;
|
|
1001
|
+
if (twitter.card) updateOrCreateMetaTag("meta[name=\"twitter:card\"]", {
|
|
1002
|
+
name: "twitter:card",
|
|
1003
|
+
content: twitter.card
|
|
1004
|
+
});
|
|
1005
|
+
if (twitter.site) updateOrCreateMetaTag("meta[name=\"twitter:site\"]", {
|
|
1006
|
+
name: "twitter:site",
|
|
1007
|
+
content: twitter.site
|
|
1008
|
+
});
|
|
1009
|
+
if (twitter.creator) updateOrCreateMetaTag("meta[name=\"twitter:creator\"]", {
|
|
1010
|
+
name: "twitter:creator",
|
|
1011
|
+
content: twitter.creator
|
|
1012
|
+
});
|
|
1013
|
+
if (twitter.title) updateOrCreateMetaTag("meta[name=\"twitter:title\"]", {
|
|
1014
|
+
name: "twitter:title",
|
|
1015
|
+
content: twitter.title
|
|
1016
|
+
});
|
|
1017
|
+
if (twitter.description) updateOrCreateMetaTag("meta[name=\"twitter:description\"]", {
|
|
1018
|
+
name: "twitter:description",
|
|
1019
|
+
content: twitter.description
|
|
1020
|
+
});
|
|
1021
|
+
if (twitter.images && twitter.images.length > 0) {
|
|
1022
|
+
document.querySelectorAll("meta[name=\"twitter:image\"]").forEach((el) => el.remove());
|
|
1023
|
+
for (const image of twitter.images) {
|
|
1024
|
+
const meta = document.createElement("meta");
|
|
1025
|
+
meta.setAttribute("name", "twitter:image");
|
|
1026
|
+
meta.setAttribute("content", image);
|
|
1027
|
+
document.head.appendChild(meta);
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
926
1032
|
function ClientRouter({ children, manifest, initialRoute }) {
|
|
927
1033
|
const [navigationState, setNavigationState] = useState(() => ({
|
|
928
1034
|
currentRoute: normalizePath(initialRoute),
|
|
@@ -1083,6 +1189,15 @@ function ClientRouter({ children, manifest, initialRoute }) {
|
|
|
1083
1189
|
cleanupAbortedNavigation(targetPath, navigationId);
|
|
1084
1190
|
return;
|
|
1085
1191
|
}
|
|
1192
|
+
try {
|
|
1193
|
+
const metadataHeader = response.headers.get("x-rari-metadata");
|
|
1194
|
+
if (metadataHeader) {
|
|
1195
|
+
const decodedMetadata = decodeURIComponent(metadataHeader);
|
|
1196
|
+
updateDocumentMetadata(JSON.parse(decodedMetadata));
|
|
1197
|
+
}
|
|
1198
|
+
} catch (metadataError) {
|
|
1199
|
+
console.warn("[ClientRouter] Failed to extract/apply metadata:", metadataError);
|
|
1200
|
+
}
|
|
1086
1201
|
const rscWireFormat = await response.text();
|
|
1087
1202
|
window.dispatchEvent(new CustomEvent("rari:navigate", { detail: {
|
|
1088
1203
|
from: fromRoute,
|
|
@@ -1443,7 +1558,6 @@ async function extractMetadata(componentPath, params, searchParams) {
|
|
|
1443
1558
|
/* @vite-ignore */
|
|
1444
1559
|
componentPath
|
|
1445
1560
|
);
|
|
1446
|
-
if (module.metadata && typeof module.metadata === "object") return module.metadata;
|
|
1447
1561
|
if (typeof module.generateMetadata === "function") {
|
|
1448
1562
|
const metadata = await module.generateMetadata({
|
|
1449
1563
|
params,
|
|
@@ -1451,6 +1565,7 @@ async function extractMetadata(componentPath, params, searchParams) {
|
|
|
1451
1565
|
});
|
|
1452
1566
|
if (metadata && typeof metadata === "object") return metadata;
|
|
1453
1567
|
}
|
|
1568
|
+
if (module.metadata && typeof module.metadata === "object") return module.metadata;
|
|
1454
1569
|
return {};
|
|
1455
1570
|
} catch (error) {
|
|
1456
1571
|
console.error(`Failed to extract metadata from ${componentPath}:`, error);
|
package/dist/vite.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { a as headers, i as rariRouter, n as defineRariOptions, o as RariResponse, r as rari, t as defineRariConfig } from "./vite-B_QMp3M4.mjs";
|
|
2
2
|
import { i as writeManifest, n as generateAppRouteManifest, r as loadManifest, t as AppRouteGenerator } from "./app-routes-DZjfJPdB.mjs";
|
|
3
|
-
import { c as createHttpRuntimeClient, d as clearPropsCacheForComponent, f as extractMetadata, g as hasServerSideDataFetching, h as extractStaticParams, i as HttpRuntimeClient, m as extractServerPropsWithCache, p as extractServerProps, u as clearPropsCache } from "./runtime-client-
|
|
3
|
+
import { c as createHttpRuntimeClient, d as clearPropsCacheForComponent, f as extractMetadata, g as hasServerSideDataFetching, h as extractStaticParams, i as HttpRuntimeClient, m as extractServerPropsWithCache, p as extractServerProps, u as clearPropsCache } from "./runtime-client-DCkfDJpL.mjs";
|
|
4
4
|
import "./server-build-ChAPR3tl.mjs";
|
|
5
5
|
|
|
6
6
|
export { AppRouteGenerator, HttpRuntimeClient, RariResponse, clearPropsCache, clearPropsCacheForComponent, createHttpRuntimeClient, defineRariConfig, defineRariOptions, extractMetadata, extractServerProps, extractServerPropsWithCache, extractStaticParams, generateAppRouteManifest, hasServerSideDataFetching, headers, loadManifest, rari, rariRouter, writeManifest };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rari",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.5.
|
|
4
|
+
"version": "0.5.18",
|
|
5
5
|
"description": "Runtime Accelerated Rendering Infrastructure (Rari)",
|
|
6
6
|
"author": "Ryan Skinner",
|
|
7
7
|
"license": "MIT",
|
|
@@ -89,11 +89,11 @@
|
|
|
89
89
|
"picocolors": "^1.1.1"
|
|
90
90
|
},
|
|
91
91
|
"optionalDependencies": {
|
|
92
|
-
"rari-darwin-arm64": "0.5.
|
|
93
|
-
"rari-darwin-x64": "0.5.
|
|
94
|
-
"rari-linux-arm64": "0.5.
|
|
95
|
-
"rari-linux-x64": "0.5.
|
|
96
|
-
"rari-win32-x64": "0.5.
|
|
92
|
+
"rari-darwin-arm64": "0.5.12",
|
|
93
|
+
"rari-darwin-x64": "0.5.12",
|
|
94
|
+
"rari-linux-arm64": "0.5.12",
|
|
95
|
+
"rari-linux-x64": "0.5.12",
|
|
96
|
+
"rari-win32-x64": "0.5.12"
|
|
97
97
|
},
|
|
98
98
|
"devDependencies": {
|
|
99
99
|
"@types/node": "^25.0.1",
|
|
@@ -12,6 +12,193 @@ import { extractPathname, findLayoutChain, isExternalUrl, matchRouteParams, norm
|
|
|
12
12
|
import { NavigationErrorOverlay } from './NavigationErrorOverlay'
|
|
13
13
|
import { StatePreserver } from './StatePreserver'
|
|
14
14
|
|
|
15
|
+
interface PageMetadata {
|
|
16
|
+
title?: string
|
|
17
|
+
description?: string
|
|
18
|
+
keywords?: string[]
|
|
19
|
+
viewport?: string
|
|
20
|
+
canonical?: string
|
|
21
|
+
openGraph?: {
|
|
22
|
+
title?: string
|
|
23
|
+
description?: string
|
|
24
|
+
url?: string
|
|
25
|
+
siteName?: string
|
|
26
|
+
images?: string[]
|
|
27
|
+
type?: string
|
|
28
|
+
}
|
|
29
|
+
twitter?: {
|
|
30
|
+
card?: string
|
|
31
|
+
site?: string
|
|
32
|
+
creator?: string
|
|
33
|
+
title?: string
|
|
34
|
+
description?: string
|
|
35
|
+
images?: string[]
|
|
36
|
+
}
|
|
37
|
+
robots?: {
|
|
38
|
+
index?: boolean
|
|
39
|
+
follow?: boolean
|
|
40
|
+
nocache?: boolean
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function updateDocumentMetadata(metadata: PageMetadata): void {
|
|
45
|
+
if (metadata.title) {
|
|
46
|
+
document.title = metadata.title
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const updateOrCreateMetaTag = (selector: string, attributes: Record<string, string>) => {
|
|
50
|
+
let element = document.querySelector(selector) as HTMLMetaElement | null
|
|
51
|
+
if (!element) {
|
|
52
|
+
element = document.createElement('meta')
|
|
53
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
54
|
+
element.setAttribute(key, value)
|
|
55
|
+
}
|
|
56
|
+
document.head.appendChild(element)
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
if (attributes.content) {
|
|
60
|
+
element.setAttribute('content', attributes.content)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (metadata.description) {
|
|
66
|
+
updateOrCreateMetaTag('meta[name="description"]', {
|
|
67
|
+
name: 'description',
|
|
68
|
+
content: metadata.description,
|
|
69
|
+
})
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (metadata.keywords && metadata.keywords.length > 0) {
|
|
73
|
+
updateOrCreateMetaTag('meta[name="keywords"]', {
|
|
74
|
+
name: 'keywords',
|
|
75
|
+
content: metadata.keywords.join(', '),
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (metadata.viewport) {
|
|
80
|
+
updateOrCreateMetaTag('meta[name="viewport"]', {
|
|
81
|
+
name: 'viewport',
|
|
82
|
+
content: metadata.viewport,
|
|
83
|
+
})
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (metadata.canonical) {
|
|
87
|
+
let canonical = document.querySelector('link[rel="canonical"]') as HTMLLinkElement | null
|
|
88
|
+
if (!canonical) {
|
|
89
|
+
canonical = document.createElement('link')
|
|
90
|
+
canonical.setAttribute('rel', 'canonical')
|
|
91
|
+
document.head.appendChild(canonical)
|
|
92
|
+
}
|
|
93
|
+
canonical.setAttribute('href', metadata.canonical)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (metadata.robots) {
|
|
97
|
+
const robotsContent: string[] = []
|
|
98
|
+
if (metadata.robots.index !== undefined) {
|
|
99
|
+
robotsContent.push(metadata.robots.index ? 'index' : 'noindex')
|
|
100
|
+
}
|
|
101
|
+
if (metadata.robots.follow !== undefined) {
|
|
102
|
+
robotsContent.push(metadata.robots.follow ? 'follow' : 'nofollow')
|
|
103
|
+
}
|
|
104
|
+
if (metadata.robots.nocache) {
|
|
105
|
+
robotsContent.push('nocache')
|
|
106
|
+
}
|
|
107
|
+
if (robotsContent.length > 0) {
|
|
108
|
+
updateOrCreateMetaTag('meta[name="robots"]', {
|
|
109
|
+
name: 'robots',
|
|
110
|
+
content: robotsContent.join(', '),
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (metadata.openGraph) {
|
|
116
|
+
const og = metadata.openGraph
|
|
117
|
+
if (og.title) {
|
|
118
|
+
updateOrCreateMetaTag('meta[property="og:title"]', {
|
|
119
|
+
property: 'og:title',
|
|
120
|
+
content: og.title,
|
|
121
|
+
})
|
|
122
|
+
}
|
|
123
|
+
if (og.description) {
|
|
124
|
+
updateOrCreateMetaTag('meta[property="og:description"]', {
|
|
125
|
+
property: 'og:description',
|
|
126
|
+
content: og.description,
|
|
127
|
+
})
|
|
128
|
+
}
|
|
129
|
+
if (og.url) {
|
|
130
|
+
updateOrCreateMetaTag('meta[property="og:url"]', {
|
|
131
|
+
property: 'og:url',
|
|
132
|
+
content: og.url,
|
|
133
|
+
})
|
|
134
|
+
}
|
|
135
|
+
if (og.siteName) {
|
|
136
|
+
updateOrCreateMetaTag('meta[property="og:site_name"]', {
|
|
137
|
+
property: 'og:site_name',
|
|
138
|
+
content: og.siteName,
|
|
139
|
+
})
|
|
140
|
+
}
|
|
141
|
+
if (og.type) {
|
|
142
|
+
updateOrCreateMetaTag('meta[property="og:type"]', {
|
|
143
|
+
property: 'og:type',
|
|
144
|
+
content: og.type,
|
|
145
|
+
})
|
|
146
|
+
}
|
|
147
|
+
if (og.images && og.images.length > 0) {
|
|
148
|
+
document.querySelectorAll('meta[property="og:image"]').forEach(el => el.remove())
|
|
149
|
+
for (const image of og.images) {
|
|
150
|
+
const meta = document.createElement('meta')
|
|
151
|
+
meta.setAttribute('property', 'og:image')
|
|
152
|
+
meta.setAttribute('content', image)
|
|
153
|
+
document.head.appendChild(meta)
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (metadata.twitter) {
|
|
159
|
+
const twitter = metadata.twitter
|
|
160
|
+
if (twitter.card) {
|
|
161
|
+
updateOrCreateMetaTag('meta[name="twitter:card"]', {
|
|
162
|
+
name: 'twitter:card',
|
|
163
|
+
content: twitter.card,
|
|
164
|
+
})
|
|
165
|
+
}
|
|
166
|
+
if (twitter.site) {
|
|
167
|
+
updateOrCreateMetaTag('meta[name="twitter:site"]', {
|
|
168
|
+
name: 'twitter:site',
|
|
169
|
+
content: twitter.site,
|
|
170
|
+
})
|
|
171
|
+
}
|
|
172
|
+
if (twitter.creator) {
|
|
173
|
+
updateOrCreateMetaTag('meta[name="twitter:creator"]', {
|
|
174
|
+
name: 'twitter:creator',
|
|
175
|
+
content: twitter.creator,
|
|
176
|
+
})
|
|
177
|
+
}
|
|
178
|
+
if (twitter.title) {
|
|
179
|
+
updateOrCreateMetaTag('meta[name="twitter:title"]', {
|
|
180
|
+
name: 'twitter:title',
|
|
181
|
+
content: twitter.title,
|
|
182
|
+
})
|
|
183
|
+
}
|
|
184
|
+
if (twitter.description) {
|
|
185
|
+
updateOrCreateMetaTag('meta[name="twitter:description"]', {
|
|
186
|
+
name: 'twitter:description',
|
|
187
|
+
content: twitter.description,
|
|
188
|
+
})
|
|
189
|
+
}
|
|
190
|
+
if (twitter.images && twitter.images.length > 0) {
|
|
191
|
+
document.querySelectorAll('meta[name="twitter:image"]').forEach(el => el.remove())
|
|
192
|
+
for (const image of twitter.images) {
|
|
193
|
+
const meta = document.createElement('meta')
|
|
194
|
+
meta.setAttribute('name', 'twitter:image')
|
|
195
|
+
meta.setAttribute('content', image)
|
|
196
|
+
document.head.appendChild(meta)
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
15
202
|
export interface ClientRouterProps {
|
|
16
203
|
children: React.ReactNode
|
|
17
204
|
manifest: AppRouteManifest
|
|
@@ -305,6 +492,18 @@ export function ClientRouter({ children, manifest, initialRoute }: ClientRouterP
|
|
|
305
492
|
return
|
|
306
493
|
}
|
|
307
494
|
|
|
495
|
+
try {
|
|
496
|
+
const metadataHeader = response.headers.get('x-rari-metadata')
|
|
497
|
+
if (metadataHeader) {
|
|
498
|
+
const decodedMetadata = decodeURIComponent(metadataHeader)
|
|
499
|
+
const metadata = JSON.parse(decodedMetadata) as PageMetadata
|
|
500
|
+
updateDocumentMetadata(metadata)
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
catch (metadataError) {
|
|
504
|
+
console.warn('[ClientRouter] Failed to extract/apply metadata:', metadataError)
|
|
505
|
+
}
|
|
506
|
+
|
|
308
507
|
const rscWireFormat = await response.text()
|
|
309
508
|
|
|
310
509
|
window.dispatchEvent(new CustomEvent('rari:navigate', {
|
package/src/router/index.ts
CHANGED
|
@@ -80,11 +80,13 @@ export type { NavigationErrorOverlayProps } from './NavigationErrorOverlay'
|
|
|
80
80
|
export {
|
|
81
81
|
clearPropsCache,
|
|
82
82
|
clearPropsCacheForComponent,
|
|
83
|
+
collectMetadataFromChain,
|
|
83
84
|
extractMetadata,
|
|
84
85
|
extractServerProps,
|
|
85
86
|
extractServerPropsWithCache,
|
|
86
87
|
extractStaticParams,
|
|
87
88
|
hasServerSideDataFetching,
|
|
89
|
+
mergeMetadata,
|
|
88
90
|
} from './props-extractor'
|
|
89
91
|
|
|
90
92
|
export type {
|
|
@@ -162,10 +162,6 @@ export async function extractMetadata(
|
|
|
162
162
|
try {
|
|
163
163
|
const module = await import(/* @vite-ignore */ componentPath)
|
|
164
164
|
|
|
165
|
-
if (module.metadata && typeof module.metadata === 'object') {
|
|
166
|
-
return module.metadata
|
|
167
|
-
}
|
|
168
|
-
|
|
169
165
|
if (typeof module.generateMetadata === 'function') {
|
|
170
166
|
const metadata = await module.generateMetadata({ params, searchParams })
|
|
171
167
|
if (metadata && typeof metadata === 'object') {
|
|
@@ -173,6 +169,10 @@ export async function extractMetadata(
|
|
|
173
169
|
}
|
|
174
170
|
}
|
|
175
171
|
|
|
172
|
+
if (module.metadata && typeof module.metadata === 'object') {
|
|
173
|
+
return module.metadata
|
|
174
|
+
}
|
|
175
|
+
|
|
176
176
|
return {}
|
|
177
177
|
}
|
|
178
178
|
catch (error) {
|
|
@@ -181,6 +181,60 @@ export async function extractMetadata(
|
|
|
181
181
|
}
|
|
182
182
|
}
|
|
183
183
|
|
|
184
|
+
export function mergeMetadata(
|
|
185
|
+
parentMetadata: MetadataResult,
|
|
186
|
+
childMetadata: MetadataResult,
|
|
187
|
+
): MetadataResult {
|
|
188
|
+
const merged: MetadataResult = { ...parentMetadata }
|
|
189
|
+
|
|
190
|
+
if (childMetadata.title !== undefined) {
|
|
191
|
+
if (typeof childMetadata.title === 'string') {
|
|
192
|
+
if (typeof parentMetadata.title === 'object' && parentMetadata.title?.template) {
|
|
193
|
+
merged.title = parentMetadata.title.template.replace('%s', childMetadata.title)
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
merged.title = childMetadata.title
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
merged.title = childMetadata.title
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (childMetadata.description !== undefined) {
|
|
205
|
+
merged.description = childMetadata.description
|
|
206
|
+
}
|
|
207
|
+
if (childMetadata.keywords !== undefined) {
|
|
208
|
+
merged.keywords = childMetadata.keywords
|
|
209
|
+
}
|
|
210
|
+
if (childMetadata.openGraph !== undefined) {
|
|
211
|
+
merged.openGraph = { ...parentMetadata.openGraph, ...childMetadata.openGraph }
|
|
212
|
+
}
|
|
213
|
+
if (childMetadata.twitter !== undefined) {
|
|
214
|
+
merged.twitter = { ...parentMetadata.twitter, ...childMetadata.twitter }
|
|
215
|
+
}
|
|
216
|
+
if (childMetadata.robots !== undefined) {
|
|
217
|
+
merged.robots = { ...parentMetadata.robots, ...childMetadata.robots }
|
|
218
|
+
}
|
|
219
|
+
if (childMetadata.icons !== undefined) {
|
|
220
|
+
merged.icons = { ...parentMetadata.icons, ...childMetadata.icons }
|
|
221
|
+
}
|
|
222
|
+
if (childMetadata.manifest !== undefined) {
|
|
223
|
+
merged.manifest = childMetadata.manifest
|
|
224
|
+
}
|
|
225
|
+
if (childMetadata.viewport !== undefined) {
|
|
226
|
+
merged.viewport = { ...parentMetadata.viewport, ...childMetadata.viewport }
|
|
227
|
+
}
|
|
228
|
+
if (childMetadata.verification !== undefined) {
|
|
229
|
+
merged.verification = { ...parentMetadata.verification, ...childMetadata.verification }
|
|
230
|
+
}
|
|
231
|
+
if (childMetadata.alternates !== undefined) {
|
|
232
|
+
merged.alternates = { ...parentMetadata.alternates, ...childMetadata.alternates }
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return merged
|
|
236
|
+
}
|
|
237
|
+
|
|
184
238
|
export async function extractStaticParams(
|
|
185
239
|
componentPath: string,
|
|
186
240
|
): Promise<StaticParamsResult> {
|
|
@@ -260,3 +314,22 @@ export function clearPropsCacheForComponent(componentPath: string): void {
|
|
|
260
314
|
}
|
|
261
315
|
}
|
|
262
316
|
}
|
|
317
|
+
|
|
318
|
+
export async function collectMetadataFromChain(
|
|
319
|
+
layoutPaths: string[],
|
|
320
|
+
pagePath: string,
|
|
321
|
+
params: Record<string, string>,
|
|
322
|
+
searchParams: Record<string, string>,
|
|
323
|
+
): Promise<MetadataResult> {
|
|
324
|
+
let metadata: MetadataResult = {}
|
|
325
|
+
|
|
326
|
+
for (const layoutPath of layoutPaths) {
|
|
327
|
+
const layoutMetadata = await extractMetadata(layoutPath, params, searchParams)
|
|
328
|
+
metadata = mergeMetadata(metadata, layoutMetadata)
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const pageMetadata = await extractMetadata(pagePath, params, searchParams)
|
|
332
|
+
metadata = mergeMetadata(metadata, pageMetadata)
|
|
333
|
+
|
|
334
|
+
return metadata
|
|
335
|
+
}
|