olova 2.0.55 → 2.0.56

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 (84) hide show
  1. package/README.md +28 -288
  2. package/dist/chunk-23UAGQ6N.js +2208 -0
  3. package/dist/chunk-23UAGQ6N.js.map +1 -0
  4. package/dist/chunk-D7SIC5TC.js +367 -0
  5. package/dist/chunk-D7SIC5TC.js.map +1 -0
  6. package/dist/entry-server.cjs +2341 -0
  7. package/dist/entry-server.cjs.map +1 -0
  8. package/dist/entry-server.js +114 -0
  9. package/dist/entry-server.js.map +1 -0
  10. package/dist/entry-worker.cjs +2354 -0
  11. package/dist/entry-worker.cjs.map +1 -0
  12. package/dist/entry-worker.js +126 -0
  13. package/dist/entry-worker.js.map +1 -0
  14. package/dist/main.cjs +18 -0
  15. package/dist/main.cjs.map +1 -0
  16. package/dist/main.js +16 -0
  17. package/dist/main.js.map +1 -0
  18. package/dist/olova.cjs +1684 -0
  19. package/dist/olova.cjs.map +1 -0
  20. package/dist/olova.d.cts +72 -0
  21. package/dist/olova.d.ts +72 -0
  22. package/dist/olova.js +1325 -0
  23. package/dist/olova.js.map +1 -0
  24. package/dist/performance.cjs +386 -0
  25. package/dist/performance.cjs.map +1 -0
  26. package/dist/performance.js +3 -0
  27. package/dist/performance.js.map +1 -0
  28. package/dist/router.cjs +646 -0
  29. package/dist/router.cjs.map +1 -0
  30. package/dist/router.d.cts +113 -0
  31. package/dist/router.d.ts +113 -0
  32. package/dist/router.js +632 -0
  33. package/dist/router.js.map +1 -0
  34. package/main.tsx +76 -0
  35. package/olova.ts +619 -0
  36. package/package.json +42 -61
  37. package/src/entry-server.tsx +165 -0
  38. package/src/entry-worker.tsx +201 -0
  39. package/src/generator/index.ts +409 -0
  40. package/src/hydration/flight.ts +320 -0
  41. package/src/hydration/index.ts +12 -0
  42. package/src/hydration/types.ts +225 -0
  43. package/src/logger.ts +182 -0
  44. package/src/main.tsx +24 -0
  45. package/src/performance.ts +488 -0
  46. package/src/plugin/index.ts +204 -0
  47. package/src/router/ErrorBoundary.tsx +145 -0
  48. package/src/router/Link.tsx +117 -0
  49. package/src/router/OlovaRouter.tsx +354 -0
  50. package/src/router/Outlet.tsx +8 -0
  51. package/src/router/context.ts +117 -0
  52. package/src/router/index.ts +29 -0
  53. package/src/router/matching.ts +63 -0
  54. package/src/router/router.tsx +23 -0
  55. package/src/router/search-params.ts +29 -0
  56. package/src/scanner/index.ts +116 -0
  57. package/src/types/index.ts +191 -0
  58. package/src/utils/export.ts +85 -0
  59. package/src/utils/index.ts +4 -0
  60. package/src/utils/naming.ts +54 -0
  61. package/src/utils/path.ts +45 -0
  62. package/tsup.config.ts +35 -0
  63. package/CHANGELOG.md +0 -31
  64. package/LICENSE +0 -21
  65. package/dist/index.cjs +0 -883
  66. package/dist/index.cjs.map +0 -1
  67. package/dist/index.d.cts +0 -138
  68. package/dist/index.d.ts +0 -138
  69. package/dist/index.js +0 -832
  70. package/dist/index.js.map +0 -1
  71. package/dist/plugin.cjs +0 -927
  72. package/dist/plugin.cjs.map +0 -1
  73. package/dist/plugin.d.cts +0 -18
  74. package/dist/plugin.d.ts +0 -18
  75. package/dist/plugin.js +0 -894
  76. package/dist/plugin.js.map +0 -1
  77. package/dist/ssg.cjs +0 -637
  78. package/dist/ssg.cjs.map +0 -1
  79. package/dist/ssg.d.cts +0 -191
  80. package/dist/ssg.d.ts +0 -191
  81. package/dist/ssg.js +0 -585
  82. package/dist/ssg.js.map +0 -1
  83. package/dist/types-BT6YsBGO.d.cts +0 -143
  84. package/dist/types-BT6YsBGO.d.ts +0 -143
package/dist/index.js DELETED
@@ -1,832 +0,0 @@
1
- // src/router.tsx
2
- import { useMemo, useEffect as useEffect3 } from "react";
3
-
4
- // src/context.tsx
5
- import { createContext, useContext, useState, useCallback, useEffect, useTransition } from "react";
6
- import { jsx } from "react/jsx-runtime";
7
- var OlovaRouterContext = createContext(null);
8
- var isBrowser = typeof window !== "undefined";
9
- var ssrContextValue = {
10
- pathname: "/",
11
- params: {},
12
- searchParams: new URLSearchParams(),
13
- navigate: () => {
14
- },
15
- replace: () => {
16
- },
17
- back: () => {
18
- },
19
- forward: () => {
20
- },
21
- refresh: () => {
22
- },
23
- prefetch: () => {
24
- },
25
- isNavigating: false
26
- };
27
- function setSSRContext(pathname, params = {}) {
28
- ssrContextValue = {
29
- pathname,
30
- params,
31
- searchParams: new URLSearchParams(),
32
- navigate: () => {
33
- },
34
- replace: () => {
35
- },
36
- back: () => {
37
- },
38
- forward: () => {
39
- },
40
- refresh: () => {
41
- },
42
- prefetch: () => {
43
- },
44
- isNavigating: false
45
- };
46
- }
47
- function useOlovaRouter() {
48
- const context = useContext(OlovaRouterContext);
49
- if (!context) {
50
- if (!isBrowser) {
51
- return ssrContextValue;
52
- }
53
- throw new Error("useOlovaRouter must be used within an OlovaRouter");
54
- }
55
- return context;
56
- }
57
- function usePathname() {
58
- return useOlovaRouter().pathname;
59
- }
60
- function useParams() {
61
- return useOlovaRouter().params;
62
- }
63
- function useSearchParams() {
64
- return useOlovaRouter().searchParams;
65
- }
66
- function useRouter() {
67
- const { navigate, replace, back, forward, refresh, prefetch, isNavigating } = useOlovaRouter();
68
- return {
69
- push: navigate,
70
- replace,
71
- back,
72
- forward,
73
- refresh,
74
- prefetch,
75
- isNavigating
76
- };
77
- }
78
- function OlovaRouterProvider({ children, basePath = "" }) {
79
- const [pathname, setPathname] = useState(() => {
80
- const path = window.location.pathname;
81
- return basePath ? path.replace(new RegExp(`^${basePath}`), "") || "/" : path;
82
- });
83
- const [searchParams, setSearchParams] = useState(() => new URLSearchParams(window.location.search));
84
- const [params, setParams] = useState({});
85
- const [isPending, startTransition] = useTransition();
86
- useEffect(() => {
87
- const handlePopState = () => {
88
- startTransition(() => {
89
- const path = window.location.pathname;
90
- setPathname(basePath ? path.replace(new RegExp(`^${basePath}`), "") || "/" : path);
91
- setSearchParams(new URLSearchParams(window.location.search));
92
- });
93
- };
94
- window.addEventListener("popstate", handlePopState);
95
- return () => window.removeEventListener("popstate", handlePopState);
96
- }, [basePath]);
97
- const navigate = useCallback((to, options = {}) => {
98
- const url = new URL(to, window.location.origin);
99
- const fullPath = basePath + url.pathname;
100
- startTransition(() => {
101
- if (options.replace) {
102
- window.history.replaceState(null, "", fullPath + url.search);
103
- } else {
104
- window.history.pushState(null, "", fullPath + url.search);
105
- }
106
- setPathname(url.pathname);
107
- setSearchParams(new URLSearchParams(url.search));
108
- if (options.scroll !== false) {
109
- window.scrollTo(0, 0);
110
- }
111
- });
112
- }, [basePath]);
113
- const replace = useCallback((to, options = {}) => {
114
- navigate(to, { ...options, replace: true });
115
- }, [navigate]);
116
- const back = useCallback(() => {
117
- window.history.back();
118
- }, []);
119
- const forward = useCallback(() => {
120
- window.history.forward();
121
- }, []);
122
- const refresh = useCallback(() => {
123
- startTransition(() => {
124
- setPathname((prev) => prev);
125
- });
126
- }, []);
127
- const prefetch = useCallback((href) => {
128
- console.log("[Olova] Prefetching:", href);
129
- }, []);
130
- const updateParams = useCallback((newParams) => {
131
- setParams(newParams);
132
- }, []);
133
- const contextValue = {
134
- pathname,
135
- params,
136
- searchParams,
137
- navigate,
138
- replace,
139
- back,
140
- forward,
141
- refresh,
142
- prefetch,
143
- isNavigating: isPending
144
- };
145
- return /* @__PURE__ */ jsx(OlovaRouterContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(OlovaParamsUpdater, { updateParams, children }) });
146
- }
147
- var ParamsUpdaterContext = createContext(null);
148
- function OlovaParamsUpdater({
149
- children,
150
- updateParams
151
- }) {
152
- return /* @__PURE__ */ jsx(ParamsUpdaterContext.Provider, { value: updateParams, children });
153
- }
154
- function useParamsUpdater() {
155
- return useContext(ParamsUpdaterContext);
156
- }
157
-
158
- // src/components/ErrorBoundary.tsx
159
- import { Component } from "react";
160
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
161
- var RouteErrorBoundary = class extends Component {
162
- constructor(props) {
163
- super(props);
164
- this.reset = () => {
165
- this.setState({ hasError: false, error: null });
166
- };
167
- this.state = { hasError: false, error: null };
168
- }
169
- static getDerivedStateFromError(error) {
170
- return { hasError: true, error };
171
- }
172
- componentDidCatch(error, errorInfo) {
173
- console.error("[Olova] Route error:", error, errorInfo);
174
- this.props.onError?.(error, errorInfo);
175
- }
176
- render() {
177
- if (this.state.hasError && this.state.error) {
178
- const FallbackComponent = this.props.fallback;
179
- if (FallbackComponent) {
180
- return /* @__PURE__ */ jsx2(FallbackComponent, { error: this.state.error, reset: this.reset });
181
- }
182
- return /* @__PURE__ */ jsx2("div", { className: "flex min-h-[400px] flex-col items-center justify-center p-8", children: /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-destructive/20 bg-destructive/5 p-6 text-center", children: [
183
- /* @__PURE__ */ jsx2("h2", { className: "mb-2 text-xl font-semibold text-destructive", children: "Something went wrong!" }),
184
- /* @__PURE__ */ jsx2("p", { className: "mb-4 text-sm text-muted-foreground", children: this.state.error.message }),
185
- /* @__PURE__ */ jsx2(
186
- "button",
187
- {
188
- onClick: this.reset,
189
- className: "rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90",
190
- children: "Try again"
191
- }
192
- )
193
- ] }) });
194
- }
195
- return this.props.children;
196
- }
197
- };
198
- function DefaultErrorComponent({ error, reset }) {
199
- return /* @__PURE__ */ jsx2("div", { className: "flex min-h-[400px] flex-col items-center justify-center p-8", children: /* @__PURE__ */ jsxs("div", { className: "max-w-md rounded-lg border border-destructive/20 bg-destructive/5 p-6 text-center", children: [
200
- /* @__PURE__ */ jsx2("div", { className: "mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-destructive/10", children: /* @__PURE__ */ jsx2(
201
- "svg",
202
- {
203
- className: "h-6 w-6 text-destructive",
204
- fill: "none",
205
- viewBox: "0 0 24 24",
206
- stroke: "currentColor",
207
- children: /* @__PURE__ */ jsx2(
208
- "path",
209
- {
210
- strokeLinecap: "round",
211
- strokeLinejoin: "round",
212
- strokeWidth: 2,
213
- d: "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
214
- }
215
- )
216
- }
217
- ) }),
218
- /* @__PURE__ */ jsx2("h2", { className: "mb-2 text-xl font-semibold text-foreground", children: "Something went wrong!" }),
219
- /* @__PURE__ */ jsx2("p", { className: "mb-4 text-sm text-muted-foreground", children: error.message }),
220
- /* @__PURE__ */ jsx2(
221
- "button",
222
- {
223
- onClick: reset,
224
- className: "rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground transition-colors hover:bg-primary/90",
225
- children: "Try again"
226
- }
227
- )
228
- ] }) });
229
- }
230
-
231
- // src/components/Loading.tsx
232
- import { Suspense } from "react";
233
- import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
234
- function RouteLoadingBoundary({ children, fallback }) {
235
- return /* @__PURE__ */ jsx3(Suspense, { fallback: fallback || /* @__PURE__ */ jsx3(DefaultLoadingComponent, {}), children });
236
- }
237
- function DefaultLoadingComponent() {
238
- return /* @__PURE__ */ jsx3("div", { className: "flex min-h-[400px] flex-col items-center justify-center p-8", children: /* @__PURE__ */ jsxs2("div", { className: "flex flex-col items-center gap-4", children: [
239
- /* @__PURE__ */ jsxs2("div", { className: "relative h-10 w-10", children: [
240
- /* @__PURE__ */ jsx3("div", { className: "absolute inset-0 animate-ping rounded-full bg-primary/20" }),
241
- /* @__PURE__ */ jsx3("div", { className: "absolute inset-0 animate-pulse rounded-full bg-primary/40" }),
242
- /* @__PURE__ */ jsx3("div", { className: "absolute inset-2 animate-spin rounded-full border-2 border-primary border-t-transparent" })
243
- ] }),
244
- /* @__PURE__ */ jsx3("p", { className: "text-sm text-muted-foreground", children: "Loading..." })
245
- ] }) });
246
- }
247
- function Skeleton({ className = "" }) {
248
- return /* @__PURE__ */ jsx3(
249
- "div",
250
- {
251
- className: `animate-pulse rounded-md bg-muted ${className}`,
252
- "aria-hidden": "true"
253
- }
254
- );
255
- }
256
- function SkeletonText({ lines = 3, className = "" }) {
257
- return /* @__PURE__ */ jsx3("div", { className: `space-y-2 ${className}`, children: Array.from({ length: lines }).map((_, i) => /* @__PURE__ */ jsx3(
258
- Skeleton,
259
- {
260
- className: `h-4 ${i === lines - 1 ? "w-3/4" : "w-full"}`
261
- },
262
- i
263
- )) });
264
- }
265
- function SkeletonCard({ className = "" }) {
266
- return /* @__PURE__ */ jsxs2("div", { className: `rounded-lg border bg-card p-6 ${className}`, children: [
267
- /* @__PURE__ */ jsx3(Skeleton, { className: "mb-4 h-6 w-1/2" }),
268
- /* @__PURE__ */ jsx3(SkeletonText, { lines: 3 })
269
- ] });
270
- }
271
-
272
- // src/components/NotFound.tsx
273
- import { Fragment, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
274
- function NotFoundBoundary({ children, fallback: Fallback, show404 }) {
275
- if (show404) {
276
- if (Fallback) {
277
- return /* @__PURE__ */ jsx4(Fallback, {});
278
- }
279
- return /* @__PURE__ */ jsx4(DefaultNotFoundComponent, {});
280
- }
281
- return /* @__PURE__ */ jsx4(Fragment, { children });
282
- }
283
- function DefaultNotFoundComponent() {
284
- return /* @__PURE__ */ jsx4("div", { className: "flex min-h-[60vh] flex-col items-center justify-center p-8", children: /* @__PURE__ */ jsxs3("div", { className: "text-center", children: [
285
- /* @__PURE__ */ jsx4("h1", { className: "mb-2 text-8xl font-bold text-primary", children: "404" }),
286
- /* @__PURE__ */ jsx4("h2", { className: "mb-4 text-2xl font-semibold text-foreground", children: "Page Not Found" }),
287
- /* @__PURE__ */ jsx4("p", { className: "mb-8 max-w-md text-muted-foreground", children: "The page you're looking for doesn't exist or has been moved." }),
288
- /* @__PURE__ */ jsxs3(
289
- "a",
290
- {
291
- href: "/",
292
- className: "inline-flex items-center gap-2 rounded-md bg-primary px-6 py-3 text-sm font-medium text-primary-foreground transition-colors hover:bg-primary/90",
293
- children: [
294
- /* @__PURE__ */ jsx4(
295
- "svg",
296
- {
297
- className: "h-4 w-4",
298
- fill: "none",
299
- viewBox: "0 0 24 24",
300
- stroke: "currentColor",
301
- children: /* @__PURE__ */ jsx4(
302
- "path",
303
- {
304
- strokeLinecap: "round",
305
- strokeLinejoin: "round",
306
- strokeWidth: 2,
307
- d: "M10 19l-7-7m0 0l7-7m-7 7h18"
308
- }
309
- )
310
- }
311
- ),
312
- "Go back home"
313
- ]
314
- }
315
- )
316
- ] }) });
317
- }
318
- function notFound() {
319
- throw new NotFoundError();
320
- }
321
- var NotFoundError = class extends Error {
322
- constructor() {
323
- super("NEXT_NOT_FOUND");
324
- this.name = "NotFoundError";
325
- }
326
- };
327
-
328
- // src/components/Metadata.tsx
329
- import { useEffect as useEffect2, useContext as useContext2, createContext as createContext2, useState as useState2, useCallback as useCallback2 } from "react";
330
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
331
- var MetadataContext = createContext2(null);
332
- function MetadataProvider({ children, rootMetadata }) {
333
- const [metadata, setMetadataState] = useState2(rootMetadata || null);
334
- const setMetadata = useCallback2((newMetadata) => {
335
- setMetadataState(newMetadata);
336
- }, []);
337
- return /* @__PURE__ */ jsxs4(MetadataContext.Provider, { value: { metadata, rootMetadata: rootMetadata || null, setMetadata }, children: [
338
- /* @__PURE__ */ jsx5(MetadataHead, { metadata, fallback: rootMetadata }),
339
- children
340
- ] });
341
- }
342
- function useMetadata() {
343
- const context = useContext2(MetadataContext);
344
- if (!context) {
345
- throw new Error("useMetadata must be used within a MetadataProvider");
346
- }
347
- return context;
348
- }
349
- function usePageMetadata(metadata) {
350
- const context = useContext2(MetadataContext);
351
- useEffect2(() => {
352
- if (context && metadata) {
353
- context.setMetadata(metadata);
354
- }
355
- return () => {
356
- if (context) {
357
- context.setMetadata(context.rootMetadata);
358
- }
359
- };
360
- }, [metadata, context]);
361
- }
362
- function MetadataHead({ metadata, fallback }) {
363
- useEffect2(() => {
364
- const activeMetadata = metadata || fallback;
365
- if (!activeMetadata) return;
366
- if (activeMetadata.title) {
367
- document.title = activeMetadata.title;
368
- }
369
- const addedMetas = [];
370
- const setMeta = (name, content, property) => {
371
- const attr = property ? "property" : "name";
372
- let meta = document.querySelector(`meta[${attr}="${name}"]`);
373
- if (!meta) {
374
- meta = document.createElement("meta");
375
- meta.setAttribute(attr, name);
376
- document.head.appendChild(meta);
377
- addedMetas.push(meta);
378
- }
379
- meta.content = content;
380
- };
381
- if (activeMetadata.description) {
382
- setMeta("description", activeMetadata.description);
383
- }
384
- if (activeMetadata.keywords?.length) {
385
- setMeta("keywords", activeMetadata.keywords.join(", "));
386
- }
387
- if (activeMetadata.authors?.length) {
388
- setMeta("author", activeMetadata.authors.map((a) => a.name).join(", "));
389
- }
390
- if (activeMetadata.robots) {
391
- setMeta("robots", activeMetadata.robots);
392
- }
393
- if (activeMetadata.openGraph) {
394
- const og = activeMetadata.openGraph;
395
- if (og.title) {
396
- setMeta("og:title", og.title, true);
397
- }
398
- if (og.description) {
399
- setMeta("og:description", og.description, true);
400
- }
401
- if (og.url) {
402
- setMeta("og:url", og.url, true);
403
- }
404
- if (og.siteName) {
405
- setMeta("og:site_name", og.siteName, true);
406
- }
407
- if (og.type) {
408
- setMeta("og:type", og.type, true);
409
- }
410
- if (og.images?.length) {
411
- const img = og.images[0];
412
- setMeta("og:image", img.url, true);
413
- if (img.width) setMeta("og:image:width", String(img.width), true);
414
- if (img.height) setMeta("og:image:height", String(img.height), true);
415
- if (img.alt) setMeta("og:image:alt", img.alt, true);
416
- }
417
- }
418
- if (activeMetadata.twitter) {
419
- const tw = activeMetadata.twitter;
420
- if (tw.card) {
421
- setMeta("twitter:card", tw.card);
422
- }
423
- if (tw.title) {
424
- setMeta("twitter:title", tw.title);
425
- }
426
- if (tw.description) {
427
- setMeta("twitter:description", tw.description);
428
- }
429
- if (tw.site) {
430
- setMeta("twitter:site", tw.site);
431
- }
432
- if (tw.creator) {
433
- setMeta("twitter:creator", tw.creator);
434
- }
435
- if (tw.images?.length) {
436
- setMeta("twitter:image", tw.images[0]);
437
- }
438
- }
439
- if (activeMetadata.canonical) {
440
- let link = document.querySelector('link[rel="canonical"]');
441
- if (!link) {
442
- link = document.createElement("link");
443
- link.rel = "canonical";
444
- document.head.appendChild(link);
445
- }
446
- link.href = activeMetadata.canonical;
447
- }
448
- return () => {
449
- addedMetas.forEach((meta) => {
450
- if (meta.parentNode) {
451
- meta.parentNode.removeChild(meta);
452
- }
453
- });
454
- };
455
- }, [metadata, fallback]);
456
- return null;
457
- }
458
- function generateMetadataHtml(metadata) {
459
- const tags = [];
460
- if (metadata.title) {
461
- tags.push(` <title>${escapeHtml(metadata.title)}</title>`);
462
- }
463
- if (metadata.description) {
464
- tags.push(` <meta name="description" content="${escapeHtml(metadata.description)}" />`);
465
- }
466
- if (metadata.keywords?.length) {
467
- tags.push(` <meta name="keywords" content="${escapeHtml(metadata.keywords.join(", "))}" />`);
468
- }
469
- if (metadata.authors?.length) {
470
- tags.push(` <meta name="author" content="${escapeHtml(metadata.authors.map((a) => a.name).join(", "))}" />`);
471
- }
472
- if (metadata.robots) {
473
- tags.push(` <meta name="robots" content="${escapeHtml(metadata.robots)}" />`);
474
- }
475
- if (metadata.openGraph) {
476
- const og = metadata.openGraph;
477
- if (og.title) tags.push(` <meta property="og:title" content="${escapeHtml(og.title)}" />`);
478
- if (og.description) tags.push(` <meta property="og:description" content="${escapeHtml(og.description)}" />`);
479
- if (og.url) tags.push(` <meta property="og:url" content="${escapeHtml(og.url)}" />`);
480
- if (og.siteName) tags.push(` <meta property="og:site_name" content="${escapeHtml(og.siteName)}" />`);
481
- if (og.type) tags.push(` <meta property="og:type" content="${escapeHtml(og.type)}" />`);
482
- if (og.images?.length) {
483
- const img = og.images[0];
484
- tags.push(` <meta property="og:image" content="${escapeHtml(img.url)}" />`);
485
- if (img.width) tags.push(` <meta property="og:image:width" content="${img.width}" />`);
486
- if (img.height) tags.push(` <meta property="og:image:height" content="${img.height}" />`);
487
- if (img.alt) tags.push(` <meta property="og:image:alt" content="${escapeHtml(img.alt)}" />`);
488
- }
489
- }
490
- if (metadata.twitter) {
491
- const tw = metadata.twitter;
492
- if (tw.card) tags.push(` <meta name="twitter:card" content="${escapeHtml(tw.card)}" />`);
493
- if (tw.title) tags.push(` <meta name="twitter:title" content="${escapeHtml(tw.title)}" />`);
494
- if (tw.description) tags.push(` <meta name="twitter:description" content="${escapeHtml(tw.description)}" />`);
495
- if (tw.site) tags.push(` <meta name="twitter:site" content="${escapeHtml(tw.site)}" />`);
496
- if (tw.creator) tags.push(` <meta name="twitter:creator" content="${escapeHtml(tw.creator)}" />`);
497
- if (tw.images?.length) tags.push(` <meta name="twitter:image" content="${escapeHtml(tw.images[0])}" />`);
498
- }
499
- if (metadata.canonical) {
500
- tags.push(` <link rel="canonical" href="${escapeHtml(metadata.canonical)}" />`);
501
- }
502
- return tags.join("\n");
503
- }
504
- function escapeHtml(text) {
505
- return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
506
- }
507
-
508
- // src/router.tsx
509
- import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
510
- function OlovaRouter({
511
- routes,
512
- basePath = "",
513
- defaultLoading,
514
- defaultNotFound,
515
- defaultError,
516
- rootMetadata,
517
- routeMetadata
518
- }) {
519
- return /* @__PURE__ */ jsx6(OlovaRouterProvider, { basePath, children: /* @__PURE__ */ jsx6(
520
- RouteRenderer,
521
- {
522
- routes,
523
- defaultLoading,
524
- defaultNotFound,
525
- defaultError,
526
- rootMetadata,
527
- routeMetadata
528
- }
529
- ) });
530
- }
531
- function RouteRenderer({
532
- routes,
533
- defaultLoading,
534
- defaultNotFound,
535
- defaultError,
536
- rootMetadata,
537
- routeMetadata
538
- }) {
539
- const pathname = usePathname();
540
- const updateParams = useParamsUpdater();
541
- const match = useMemo(() => {
542
- return matchRoute(routes, pathname);
543
- }, [routes, pathname]);
544
- useEffect3(() => {
545
- if (updateParams && match) {
546
- updateParams(match.params);
547
- }
548
- }, [match, updateParams]);
549
- const currentMetadata = useMemo(() => {
550
- if (!routeMetadata) return null;
551
- if (routeMetadata[pathname]) {
552
- return routeMetadata[pathname];
553
- }
554
- return null;
555
- }, [pathname, routeMetadata]);
556
- if (!match) {
557
- const NotFoundComponent = defaultNotFound || DefaultNotFoundComponent;
558
- return /* @__PURE__ */ jsxs5(Fragment2, { children: [
559
- /* @__PURE__ */ jsx6(MetadataHead, { metadata: currentMetadata, fallback: rootMetadata }),
560
- /* @__PURE__ */ jsx6(NotFoundComponent, {})
561
- ] });
562
- }
563
- return /* @__PURE__ */ jsxs5(Fragment2, { children: [
564
- /* @__PURE__ */ jsx6(MetadataHead, { metadata: currentMetadata, fallback: rootMetadata }),
565
- /* @__PURE__ */ jsx6(
566
- LayoutRenderer,
567
- {
568
- match,
569
- routes,
570
- defaultLoading,
571
- defaultError
572
- }
573
- )
574
- ] });
575
- }
576
- function LayoutRenderer({ match, routes, defaultLoading, defaultError }) {
577
- const { route, params } = match;
578
- const layoutChain = useMemo(() => {
579
- return findLayoutChain(routes, route);
580
- }, [routes, route]);
581
- const renderSlots = (currentRoute) => {
582
- const slots = {};
583
- if (currentRoute.slots) {
584
- for (const [slotName, slotRoute] of Object.entries(currentRoute.slots)) {
585
- if (slotRoute.component) {
586
- const SlotComponent = slotRoute.component;
587
- const SlotLoading = slotRoute.loading;
588
- const SlotError = slotRoute.error || defaultError;
589
- slots[slotName] = /* @__PURE__ */ jsx6(RouteErrorBoundary, { fallback: SlotError, children: /* @__PURE__ */ jsx6(RouteLoadingBoundary, { fallback: SlotLoading ? /* @__PURE__ */ jsx6(SlotLoading, {}) : defaultLoading, children: /* @__PURE__ */ jsx6(SlotComponent, { params, searchParams: {} }) }) }, slotName);
590
- }
591
- }
592
- }
593
- return slots;
594
- };
595
- const renderLayouts = (index) => {
596
- if (index >= layoutChain.length) {
597
- if (!route.component) {
598
- return null;
599
- }
600
- const PageComponent = route.component;
601
- const LoadingComponent2 = route.loading;
602
- const ErrorComponent2 = route.error || defaultError;
603
- return /* @__PURE__ */ jsx6(RouteErrorBoundary, { fallback: ErrorComponent2, children: /* @__PURE__ */ jsx6(RouteLoadingBoundary, { fallback: LoadingComponent2 ? /* @__PURE__ */ jsx6(LoadingComponent2, {}) : defaultLoading, children: /* @__PURE__ */ jsx6(PageComponent, { params, searchParams: {} }) }) });
604
- }
605
- const currentLayout = layoutChain[index];
606
- if (!currentLayout.layout) {
607
- return renderLayouts(index + 1);
608
- }
609
- const LayoutComponent = currentLayout.layout;
610
- const LoadingComponent = currentLayout.loading;
611
- const ErrorComponent = currentLayout.error || defaultError;
612
- const slots = renderSlots(currentLayout);
613
- return /* @__PURE__ */ jsx6(RouteErrorBoundary, { fallback: ErrorComponent, children: /* @__PURE__ */ jsx6(RouteLoadingBoundary, { fallback: LoadingComponent ? /* @__PURE__ */ jsx6(LoadingComponent, {}) : defaultLoading, children: /* @__PURE__ */ jsx6(LayoutComponent, { params, ...slots, children: renderLayouts(index + 1) }) }) });
614
- };
615
- return /* @__PURE__ */ jsx6(Fragment2, { children: renderLayouts(0) });
616
- }
617
- function matchRoute(routes, pathname) {
618
- const segments = pathname.split("/").filter(Boolean);
619
- function matchRecursive(routeList, segmentIndex, params) {
620
- if (segmentIndex >= segments.length) {
621
- for (const route of routeList) {
622
- if (route.path === "" || route.path === "/") {
623
- if (route.component) {
624
- return {
625
- route,
626
- params,
627
- pathname,
628
- searchParams: new URLSearchParams(window.location.search)
629
- };
630
- }
631
- }
632
- }
633
- return null;
634
- }
635
- const currentSegment = segments[segmentIndex];
636
- for (const route of routeList) {
637
- if (route.path.startsWith("(") && route.path.endsWith(")")) {
638
- if (route.children.length > 0) {
639
- const childMatch = matchRecursive(route.children, segmentIndex, params);
640
- if (childMatch) return childMatch;
641
- }
642
- continue;
643
- }
644
- if (route.isCatchAll && route.paramName) {
645
- const remainingSegments = segments.slice(segmentIndex);
646
- const newParams = { ...params, [route.paramName]: remainingSegments.join("/") };
647
- if (route.component) {
648
- return {
649
- route,
650
- params: newParams,
651
- pathname,
652
- searchParams: new URLSearchParams(window.location.search)
653
- };
654
- }
655
- }
656
- if (route.isDynamic && route.paramName) {
657
- const newParams = { ...params, [route.paramName]: currentSegment };
658
- if (segmentIndex === segments.length - 1) {
659
- if (route.component) {
660
- return {
661
- route,
662
- params: newParams,
663
- pathname,
664
- searchParams: new URLSearchParams(window.location.search)
665
- };
666
- }
667
- }
668
- if (route.children.length > 0) {
669
- const childMatch = matchRecursive(route.children, segmentIndex + 1, newParams);
670
- if (childMatch) return childMatch;
671
- }
672
- }
673
- if (route.path === currentSegment) {
674
- if (segmentIndex === segments.length - 1) {
675
- if (route.component) {
676
- return {
677
- route,
678
- params,
679
- pathname,
680
- searchParams: new URLSearchParams(window.location.search)
681
- };
682
- }
683
- for (const child of route.children) {
684
- if ((child.path === "" || child.path === "/") && child.component) {
685
- return {
686
- route: child,
687
- params,
688
- pathname,
689
- searchParams: new URLSearchParams(window.location.search)
690
- };
691
- }
692
- }
693
- }
694
- if (route.children.length > 0) {
695
- const childMatch = matchRecursive(route.children, segmentIndex + 1, params);
696
- if (childMatch) return childMatch;
697
- }
698
- }
699
- }
700
- return null;
701
- }
702
- if (pathname === "/" || pathname === "") {
703
- for (const route of routes) {
704
- if ((route.path === "" || route.path === "/") && route.component) {
705
- return {
706
- route,
707
- params: {},
708
- pathname: "/",
709
- searchParams: new URLSearchParams(window.location.search)
710
- };
711
- }
712
- }
713
- }
714
- return matchRecursive(routes, 0, {});
715
- }
716
- function findLayoutChain(routes, targetRoute) {
717
- const chain = [];
718
- function findRecursive(routeList, path) {
719
- for (const route of routeList) {
720
- const currentPath = [...path, route];
721
- if (route.id === targetRoute.id) {
722
- for (const r of currentPath) {
723
- if (r.layout) {
724
- chain.push(r);
725
- }
726
- }
727
- return true;
728
- }
729
- if (route.children.length > 0) {
730
- if (findRecursive(route.children, currentPath)) {
731
- return true;
732
- }
733
- }
734
- }
735
- return false;
736
- }
737
- findRecursive(routes, []);
738
- return chain;
739
- }
740
-
741
- // src/components/Link.tsx
742
- import { forwardRef, useCallback as useCallback3 } from "react";
743
- import { jsx as jsx7 } from "react/jsx-runtime";
744
- var Link = forwardRef(
745
- ({ href, replace = false, scroll = true, prefetch = true, children, onClick, ...props }, ref) => {
746
- const router = useOlovaRouter();
747
- const handleClick = useCallback3(
748
- (e) => {
749
- onClick?.(e);
750
- if (e.defaultPrevented || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey || e.button !== 0) {
751
- return;
752
- }
753
- if (href.startsWith("http://") || href.startsWith("https://") || href.startsWith("//")) {
754
- return;
755
- }
756
- if (href.startsWith("#")) {
757
- return;
758
- }
759
- e.preventDefault();
760
- if (replace) {
761
- router.replace(href, { scroll });
762
- } else {
763
- router.navigate(href, { scroll });
764
- }
765
- },
766
- [href, replace, scroll, router, onClick]
767
- );
768
- const handleMouseEnter = useCallback3(() => {
769
- if (prefetch && !href.startsWith("http")) {
770
- router.prefetch(href);
771
- }
772
- }, [href, prefetch, router]);
773
- return /* @__PURE__ */ jsx7(
774
- "a",
775
- {
776
- ref,
777
- href,
778
- onClick: handleClick,
779
- onMouseEnter: handleMouseEnter,
780
- ...props,
781
- children
782
- }
783
- );
784
- }
785
- );
786
- Link.displayName = "Link";
787
- var NavLink = forwardRef(
788
- ({ href, activeClassName = "", inactiveClassName = "", exact = false, className = "", ...props }, ref) => {
789
- const { pathname } = useOlovaRouter();
790
- const isActive = exact ? pathname === href : pathname.startsWith(href) && (href === "/" ? pathname === "/" : true);
791
- const combinedClassName = `${className} ${isActive ? activeClassName : inactiveClassName}`.trim();
792
- return /* @__PURE__ */ jsx7(
793
- Link,
794
- {
795
- ref,
796
- href,
797
- className: combinedClassName,
798
- "aria-current": isActive ? "page" : void 0,
799
- ...props
800
- }
801
- );
802
- }
803
- );
804
- NavLink.displayName = "NavLink";
805
- export {
806
- DefaultErrorComponent,
807
- DefaultLoadingComponent,
808
- DefaultNotFoundComponent,
809
- Link,
810
- MetadataHead,
811
- MetadataProvider,
812
- NavLink,
813
- NotFoundBoundary,
814
- NotFoundError,
815
- OlovaRouter,
816
- RouteErrorBoundary,
817
- RouteLoadingBoundary,
818
- Skeleton,
819
- SkeletonCard,
820
- SkeletonText,
821
- generateMetadataHtml,
822
- notFound,
823
- setSSRContext,
824
- useMetadata,
825
- useOlovaRouter,
826
- usePageMetadata,
827
- useParams,
828
- usePathname,
829
- useRouter,
830
- useSearchParams
831
- };
832
- //# sourceMappingURL=index.js.map