olova 2.0.54 → 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 (96) hide show
  1. package/README.md +34 -283
  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 +37 -55
  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/dist/client-BBLXpllK.d.ts +0 -487
  64. package/dist/client-C0av_vTZ.d.cts +0 -487
  65. package/dist/client.cjs +0 -850
  66. package/dist/client.cjs.map +0 -1
  67. package/dist/client.d.cts +0 -5
  68. package/dist/client.d.ts +0 -5
  69. package/dist/client.js +0 -816
  70. package/dist/client.js.map +0 -1
  71. package/dist/image.cjs +0 -25
  72. package/dist/image.cjs.map +0 -1
  73. package/dist/image.d.cts +0 -1
  74. package/dist/image.d.ts +0 -1
  75. package/dist/image.js +0 -3
  76. package/dist/image.js.map +0 -1
  77. package/dist/index.cjs +0 -907
  78. package/dist/index.cjs.map +0 -1
  79. package/dist/index.d.cts +0 -31
  80. package/dist/index.d.ts +0 -31
  81. package/dist/index.js +0 -868
  82. package/dist/index.js.map +0 -1
  83. package/dist/plugin/index.cjs +0 -1550
  84. package/dist/plugin/index.cjs.map +0 -1
  85. package/dist/plugin/index.d.cts +0 -131
  86. package/dist/plugin/index.d.ts +0 -131
  87. package/dist/plugin/index.js +0 -1490
  88. package/dist/plugin/index.js.map +0 -1
  89. package/dist/serialization-xKcOESDh.d.cts +0 -122
  90. package/dist/serialization-xKcOESDh.d.ts +0 -122
  91. package/dist/server.cjs +0 -547
  92. package/dist/server.cjs.map +0 -1
  93. package/dist/server.d.cts +0 -148
  94. package/dist/server.d.ts +0 -148
  95. package/dist/server.js +0 -493
  96. package/dist/server.js.map +0 -1
package/dist/index.js DELETED
@@ -1,868 +0,0 @@
1
- var __defProp = Object.defineProperty;
2
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
- var __getOwnPropNames = Object.getOwnPropertyNames;
4
- var __hasOwnProp = Object.prototype.hasOwnProperty;
5
- var __esm = (fn, res) => function __init() {
6
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
7
- };
8
- var __export = (target, all) => {
9
- for (var name in all)
10
- __defProp(target, name, { get: all[name], enumerable: true });
11
- };
12
- var __copyProps = (to, from, except, desc) => {
13
- if (from && typeof from === "object" || typeof from === "function") {
14
- for (let key of __getOwnPropNames(from))
15
- if (!__hasOwnProp.call(to, key) && key !== except)
16
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
- }
18
- return to;
19
- };
20
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
21
-
22
- // src/hydration/scheduler.ts
23
- var scheduler_exports = {};
24
- __export(scheduler_exports, {
25
- HydrationScheduler: () => HydrationScheduler
26
- });
27
- var HydrationScheduler;
28
- var init_scheduler = __esm({
29
- "src/hydration/scheduler.ts"() {
30
- "use strict";
31
- HydrationScheduler = class {
32
- constructor() {
33
- this.queue = [];
34
- this.isProcessing = false;
35
- this.state = "idle";
36
- if (typeof window !== "undefined") {
37
- window.__FLIGHT_CHUNK__ = this.receiveChunk.bind(this);
38
- }
39
- }
40
- getState() {
41
- return this.state;
42
- }
43
- schedule(id, priority, task) {
44
- const priorityValue = {
45
- critical: 0,
46
- high: 1,
47
- low: 2,
48
- idle: 3
49
- }[priority];
50
- this.queue.push({ id, priority: priorityValue, task });
51
- this.queue.sort((a, b) => a.priority - b.priority);
52
- if (!this.isProcessing) {
53
- this.processQueue();
54
- }
55
- }
56
- async processQueue() {
57
- this.isProcessing = true;
58
- this.state = "hydrating";
59
- while (this.queue.length > 0) {
60
- const item = this.queue.shift();
61
- try {
62
- if (item.priority >= 2 && "requestIdleCallback" in window) {
63
- await new Promise((resolve) => {
64
- window.requestIdleCallback(() => {
65
- item.task();
66
- resolve();
67
- });
68
- });
69
- } else {
70
- await Promise.resolve().then(item.task);
71
- }
72
- } catch (error) {
73
- console.error(`[Olova] Hydration failed for ${item.id}:`, error);
74
- }
75
- await new Promise((r) => setTimeout(r, 0));
76
- }
77
- this.isProcessing = false;
78
- this.state = "complete";
79
- if (typeof window !== "undefined") {
80
- window.dispatchEvent(new CustomEvent("olova:hydration-complete"));
81
- }
82
- }
83
- receiveChunk(chunk) {
84
- this.state = "streaming";
85
- const placeholder = document.querySelector(`[data-flight-id="${chunk.id}"]`);
86
- if (placeholder) {
87
- const temp = document.createElement("div");
88
- temp.innerHTML = chunk.html;
89
- while (temp.firstChild) {
90
- placeholder.parentNode?.insertBefore(temp.firstChild, placeholder);
91
- }
92
- placeholder.remove();
93
- if (chunk.data) {
94
- this.schedule(chunk.id, "high", () => {
95
- this.hydrateNode(chunk.id, chunk.data);
96
- });
97
- }
98
- }
99
- }
100
- hydrateNode(id, data) {
101
- const element = document.querySelector(`[data-flight-id="${id}"]`);
102
- if (element) {
103
- element.setAttribute("data-hydrated", "true");
104
- element.__FLIGHT_DATA__ = data;
105
- }
106
- }
107
- };
108
- }
109
- });
110
-
111
- // src/core/config.ts
112
- var defaultConfig = {
113
- outDir: ".olova/dist",
114
- basePath: "/",
115
- trailingSlash: false,
116
- experimental: {
117
- prefetch: true,
118
- serverComponents: false
119
- },
120
- staticGeneration: {
121
- timeout: 3e4,
122
- fallback: "blocking"
123
- }
124
- };
125
- function defineConfig(config = {}) {
126
- return {
127
- ...defaultConfig,
128
- ...config,
129
- experimental: {
130
- ...defaultConfig.experimental,
131
- ...config.experimental
132
- },
133
- staticGeneration: {
134
- ...defaultConfig.staticGeneration,
135
- ...config.staticGeneration
136
- }
137
- };
138
- }
139
-
140
- // src/router/context.ts
141
- import { createContext } from "react";
142
- var pageCache = /* @__PURE__ */ new Map();
143
- var prefetchPromises = /* @__PURE__ */ new Map();
144
- var metadataCache = /* @__PURE__ */ new Map();
145
- var NAVIGATE_EVENT = "olova:navigate";
146
- var eventHandlers = /* @__PURE__ */ new Map();
147
- var routerEvents = {
148
- on(event, handler) {
149
- if (!eventHandlers.has(event)) {
150
- eventHandlers.set(event, /* @__PURE__ */ new Set());
151
- }
152
- eventHandlers.get(event).add(handler);
153
- },
154
- off(event, handler) {
155
- eventHandlers.get(event)?.delete(handler);
156
- },
157
- emit(event, ...args) {
158
- eventHandlers.get(event)?.forEach((handler) => handler(...args));
159
- }
160
- };
161
- var RouterContext = createContext({
162
- pathname: "/",
163
- params: {},
164
- query: {},
165
- asPath: "/",
166
- push: async () => true,
167
- replace: async () => true,
168
- back: () => {
169
- },
170
- forward: () => {
171
- },
172
- prefetch: async () => {
173
- },
174
- reload: () => {
175
- },
176
- isReady: false,
177
- isNavigating: false,
178
- isFallback: false,
179
- events: routerEvents
180
- });
181
- var ParamsContext = createContext({});
182
-
183
- // src/router/navigation.ts
184
- function navigate(to, options = {}) {
185
- return new Promise((resolve) => {
186
- const { scroll = true } = options;
187
- routerEvents.emit("routeChangeStart", to, { shallow: options.shallow });
188
- routerEvents.emit("beforeHistoryChange", to);
189
- window.history.pushState({ scroll }, "", to);
190
- window.dispatchEvent(new CustomEvent(NAVIGATE_EVENT, {
191
- detail: { path: to, scroll }
192
- }));
193
- if (scroll) {
194
- window.scrollTo(0, 0);
195
- }
196
- routerEvents.emit("routeChangeComplete", to, { shallow: options.shallow });
197
- resolve(true);
198
- });
199
- }
200
- function replace(to, options = {}) {
201
- return new Promise((resolve) => {
202
- const { scroll = false } = options;
203
- routerEvents.emit("routeChangeStart", to, { shallow: options.shallow });
204
- routerEvents.emit("beforeHistoryChange", to);
205
- window.history.replaceState({ scroll }, "", to);
206
- window.dispatchEvent(new CustomEvent(NAVIGATE_EVENT, {
207
- detail: { path: to, scroll, replace: true }
208
- }));
209
- if (scroll) {
210
- window.scrollTo(0, 0);
211
- }
212
- routerEvents.emit("routeChangeComplete", to, { shallow: options.shallow });
213
- resolve(true);
214
- });
215
- }
216
- function redirect(url) {
217
- if (typeof window !== "undefined") {
218
- window.location.href = url;
219
- }
220
- throw new Error("NEXT_REDIRECT");
221
- }
222
- function permanentRedirect(url) {
223
- redirect(url);
224
- }
225
- function notFound() {
226
- throw new Error("NEXT_NOT_FOUND");
227
- }
228
-
229
- // src/router/match.ts
230
- function matchRoute(routePath, currentPath) {
231
- if (routePath === currentPath) {
232
- return { matches: true, params: {}, score: 100 };
233
- }
234
- const routeSegments = routePath.split("/").filter(Boolean);
235
- const pathSegments = currentPath.split("/").filter(Boolean);
236
- const params = {};
237
- let score = 0;
238
- for (let i = 0; i < routeSegments.length; i++) {
239
- const routeSegment = routeSegments[i];
240
- const pathSegment = pathSegments[i];
241
- if (routeSegment?.startsWith("*") && routeSegment?.endsWith("?")) {
242
- const paramName = routeSegment.slice(1, -1);
243
- const remainingPath = pathSegments.slice(i);
244
- params[paramName] = remainingPath.length > 0 ? remainingPath : [];
245
- score += 1;
246
- return { matches: true, params, score };
247
- }
248
- if (routeSegment?.startsWith("*")) {
249
- const paramName = routeSegment.slice(1);
250
- const remainingPath = pathSegments.slice(i);
251
- if (remainingPath.length > 0) {
252
- params[paramName] = remainingPath;
253
- score += 2;
254
- return { matches: true, params, score };
255
- }
256
- return { matches: false, params: {}, score: 0 };
257
- }
258
- if (routeSegment?.startsWith(":") || routeSegment?.startsWith("[") && routeSegment?.endsWith("]")) {
259
- if (!pathSegment) return { matches: false, params: {}, score: 0 };
260
- const paramName = routeSegment.startsWith(":") ? routeSegment.slice(1) : routeSegment.slice(1, -1);
261
- params[paramName] = pathSegment;
262
- score += 5;
263
- continue;
264
- }
265
- if (routeSegment !== pathSegment) return { matches: false, params: {}, score: 0 };
266
- score += 10;
267
- }
268
- if (routeSegments.length !== pathSegments.length) return { matches: false, params: {}, score: 0 };
269
- return { matches: true, params, score };
270
- }
271
- function findRoute(routes, path) {
272
- const normalizedPath = path.endsWith("/") && path !== "/" ? path.slice(0, -1) : path;
273
- const exactMatch = routes.find((r) => r.path === normalizedPath);
274
- if (exactMatch) return { route: exactMatch, params: {} };
275
- let bestMatch = null;
276
- for (const route of routes) {
277
- const { matches, params, score } = matchRoute(route.path, normalizedPath);
278
- if (matches && (!bestMatch || score > bestMatch.score)) {
279
- bestMatch = { route, params, score };
280
- }
281
- }
282
- return bestMatch ? { route: bestMatch.route, params: bestMatch.params } : { route: null, params: {} };
283
- }
284
-
285
- // src/router/prefetch.ts
286
- async function prefetchRoute(routes, path) {
287
- const normalizedPath = path.endsWith("/") && path !== "/" ? path.slice(0, -1) : path;
288
- if (prefetchPromises.has(normalizedPath)) {
289
- return prefetchPromises.get(normalizedPath);
290
- }
291
- const { route } = findRoute(routes, normalizedPath);
292
- if (!route) return;
293
- const cacheKey = route.path;
294
- if (pageCache.has(cacheKey)) return;
295
- const promise = route.component().then((module) => {
296
- pageCache.set(cacheKey, module.default);
297
- if (module.metadata) {
298
- metadataCache.set(cacheKey, module.metadata);
299
- }
300
- }).catch(() => {
301
- });
302
- prefetchPromises.set(normalizedPath, promise);
303
- return promise;
304
- }
305
-
306
- // src/router/hooks.ts
307
- import { useState, useEffect, useContext } from "react";
308
- function useRouter() {
309
- return useContext(RouterContext);
310
- }
311
- function useParams() {
312
- return useContext(ParamsContext);
313
- }
314
- function usePathname() {
315
- const { pathname } = useContext(RouterContext);
316
- return pathname;
317
- }
318
- function useSearchParams() {
319
- const [searchParams, setSearchParams] = useState(
320
- () => new URLSearchParams(window.location.search)
321
- );
322
- useEffect(() => {
323
- const handleNavigate = () => {
324
- setSearchParams(new URLSearchParams(window.location.search));
325
- };
326
- window.addEventListener(NAVIGATE_EVENT, handleNavigate);
327
- window.addEventListener("popstate", handleNavigate);
328
- return () => {
329
- window.removeEventListener(NAVIGATE_EVENT, handleNavigate);
330
- window.removeEventListener("popstate", handleNavigate);
331
- };
332
- }, []);
333
- return searchParams;
334
- }
335
-
336
- // src/router/Link.tsx
337
- import { useEffect as useEffect2, useCallback, useRef } from "react";
338
- import { jsx } from "react/jsx-runtime";
339
- function Link({
340
- href,
341
- children,
342
- prefetch: shouldPrefetch = true,
343
- scroll = true,
344
- replace: shouldReplace = false,
345
- shallow = false,
346
- ...props
347
- }) {
348
- const prefetchedRef = useRef(false);
349
- const linkRef = useRef(null);
350
- const getRoutes = useCallback(() => {
351
- return window.__OLOVA_ROUTES__ || [];
352
- }, []);
353
- useEffect2(() => {
354
- if (!shouldPrefetch || prefetchedRef.current || !linkRef.current) return;
355
- const observer = new IntersectionObserver(
356
- (entries) => {
357
- if (entries[0].isIntersecting && !prefetchedRef.current) {
358
- prefetchedRef.current = true;
359
- prefetchRoute(getRoutes(), href);
360
- }
361
- },
362
- { rootMargin: "200px" }
363
- );
364
- observer.observe(linkRef.current);
365
- return () => observer.disconnect();
366
- }, [href, shouldPrefetch, getRoutes]);
367
- const handleMouseEnter = useCallback(() => {
368
- if (!prefetchedRef.current) {
369
- prefetchedRef.current = true;
370
- prefetchRoute(getRoutes(), href);
371
- }
372
- }, [href, getRoutes]);
373
- const handleClick = useCallback((e) => {
374
- if (e.ctrlKey || e.metaKey || e.shiftKey || e.button !== 0 || props.target === "_blank" || href.startsWith("http") || href.startsWith("//")) {
375
- return;
376
- }
377
- e.preventDefault();
378
- if (shouldReplace) {
379
- replace(href, { scroll, shallow });
380
- } else {
381
- navigate(href, { scroll, shallow });
382
- }
383
- }, [href, scroll, shallow, shouldReplace, props.target]);
384
- return /* @__PURE__ */ jsx(
385
- "a",
386
- {
387
- ref: linkRef,
388
- href,
389
- onClick: handleClick,
390
- onMouseEnter: handleMouseEnter,
391
- ...props,
392
- children
393
- }
394
- );
395
- }
396
-
397
- // src/router/Router.tsx
398
- import React2, { Suspense, useMemo, useEffect as useEffect4, useRef as useRef3 } from "react";
399
-
400
- // src/router/BuiltinComponents.tsx
401
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
402
- function NotFound() {
403
- return /* @__PURE__ */ jsxs("div", { style: { padding: "4rem 2rem", textAlign: "center" }, children: [
404
- /* @__PURE__ */ jsx2("h1", { style: { fontSize: "4rem", margin: 0 }, children: "404" }),
405
- /* @__PURE__ */ jsx2("p", { style: { fontSize: "1.25rem", color: "#666" }, children: "This page could not be found." }),
406
- /* @__PURE__ */ jsx2(Link, { href: "/", style: { color: "#0070f3" }, children: "Go back home" })
407
- ] });
408
- }
409
- function ErrorPage() {
410
- return /* @__PURE__ */ jsxs("div", { style: { padding: "4rem 2rem", textAlign: "center" }, children: [
411
- /* @__PURE__ */ jsx2("h1", { style: { fontSize: "2rem", margin: 0 }, children: "Something went wrong" }),
412
- /* @__PURE__ */ jsx2("p", { style: { fontSize: "1rem", color: "#666" }, children: "Failed to load this page." }),
413
- /* @__PURE__ */ jsx2("button", { onClick: () => window.location.reload(), style: {
414
- padding: "0.5rem 1rem",
415
- background: "#0070f3",
416
- color: "white",
417
- border: "none",
418
- borderRadius: "4px",
419
- cursor: "pointer"
420
- }, children: "Try again" })
421
- ] });
422
- }
423
- function LoadingIndicator() {
424
- return /* @__PURE__ */ jsx2("div", { style: { padding: "2rem", textAlign: "center" }, children: /* @__PURE__ */ jsx2("div", { style: {
425
- width: "40px",
426
- height: "40px",
427
- border: "3px solid #f3f3f3",
428
- borderTop: "3px solid #0070f3",
429
- borderRadius: "50%",
430
- animation: "olova-spin 1s linear infinite",
431
- margin: "0 auto"
432
- } }) });
433
- }
434
- function NavigationProgress() {
435
- return /* @__PURE__ */ jsx2("div", { style: {
436
- position: "fixed",
437
- top: 0,
438
- left: 0,
439
- right: 0,
440
- height: "3px",
441
- background: "linear-gradient(90deg, #0070f3, #00a8ff)",
442
- animation: "olova-loading 1s ease-in-out infinite",
443
- zIndex: 9999
444
- } });
445
- }
446
-
447
- // src/router/Styles.ts
448
- function injectStyles() {
449
- if (typeof document !== "undefined") {
450
- const style = document.createElement("style");
451
- style.textContent = `
452
- @keyframes olova-loading {
453
- 0% { transform: translateX(-100%); }
454
- 50% { transform: translateX(0); }
455
- 100% { transform: translateX(100%); }
456
- }
457
- @keyframes olova-spin {
458
- 0% { transform: rotate(0deg); }
459
- 100% { transform: rotate(360deg); }
460
- }
461
- `;
462
- document.head.appendChild(style);
463
- }
464
- }
465
-
466
- // src/router/useRouterInternal.ts
467
- import { useState as useState2, useEffect as useEffect3, useRef as useRef2, startTransition } from "react";
468
-
469
- // src/router/Metadata.ts
470
- function applyMetadata(metadata) {
471
- if (!metadata || typeof document === "undefined") return;
472
- if (metadata.title) {
473
- document.title = metadata.title;
474
- }
475
- const metaTags = {
476
- description: metadata.description,
477
- keywords: metadata.keywords
478
- };
479
- for (const [name, content] of Object.entries(metaTags)) {
480
- if (content) {
481
- let meta = document.querySelector(`meta[name="${name}"]`);
482
- if (!meta) {
483
- meta = document.createElement("meta");
484
- meta.setAttribute("name", name);
485
- document.head.appendChild(meta);
486
- }
487
- meta.setAttribute("content", content);
488
- }
489
- }
490
- if (metadata.openGraph) {
491
- const og = metadata.openGraph;
492
- const ogTags = {
493
- "og:title": og.title,
494
- "og:description": og.description,
495
- "og:type": og.type,
496
- "og:url": og.url,
497
- "og:image": og.image
498
- };
499
- for (const [property, content] of Object.entries(ogTags)) {
500
- if (content) {
501
- let meta = document.querySelector(`meta[property="${property}"]`);
502
- if (!meta) {
503
- meta = document.createElement("meta");
504
- meta.setAttribute("property", property);
505
- document.head.appendChild(meta);
506
- }
507
- meta.setAttribute("content", content);
508
- }
509
- }
510
- }
511
- }
512
-
513
- // src/router/Query.ts
514
- function parseQueryString(search) {
515
- const params = new URLSearchParams(search);
516
- const result = {};
517
- params.forEach((value, key) => {
518
- if (result[key]) {
519
- if (Array.isArray(result[key])) {
520
- result[key].push(value);
521
- } else {
522
- result[key] = [result[key], value];
523
- }
524
- } else {
525
- result[key] = value;
526
- }
527
- });
528
- return result;
529
- }
530
-
531
- // src/router/useRouterInternal.ts
532
- function useRouterInternal(routes) {
533
- const [currentPath, setCurrentPath] = useState2(() => {
534
- if (typeof window === "undefined") return "/";
535
- const path = window.location.pathname;
536
- return path.endsWith("/") && path !== "/" ? path.slice(0, -1) : path;
537
- });
538
- const [query, setQuery] = useState2(() => {
539
- if (typeof window === "undefined") return {};
540
- return parseQueryString(window.location.search);
541
- });
542
- const [components, setComponents] = useState2({ page: null, layouts: [] });
543
- const [params, setParams] = useState2({});
544
- const [isNavigating, setIsNavigating] = useState2(false);
545
- const [isReady, setIsReady] = useState2(false);
546
- const isHydrating = useRef2(true);
547
- useEffect3(() => {
548
- const handleNavigate = (e) => {
549
- const customEvent = e;
550
- const newPath = customEvent.detail?.path || window.location.pathname;
551
- const normalizedPath = newPath.endsWith("/") && newPath !== "/" ? newPath.slice(0, -1) : newPath;
552
- isHydrating.current = false;
553
- startTransition(() => {
554
- setCurrentPath(normalizedPath);
555
- setQuery(parseQueryString(window.location.search));
556
- });
557
- };
558
- const handlePopState = () => {
559
- const path = window.location.pathname;
560
- const normalizedPath = path.endsWith("/") && path !== "/" ? path.slice(0, -1) : path;
561
- isHydrating.current = false;
562
- startTransition(() => {
563
- setCurrentPath(normalizedPath);
564
- setQuery(parseQueryString(window.location.search));
565
- });
566
- };
567
- window.addEventListener(NAVIGATE_EVENT, handleNavigate);
568
- window.addEventListener("popstate", handlePopState);
569
- return () => {
570
- window.removeEventListener(NAVIGATE_EVENT, handleNavigate);
571
- window.removeEventListener("popstate", handlePopState);
572
- };
573
- }, []);
574
- useEffect3(() => {
575
- const { route, params: extractedParams } = findRoute(routes, currentPath);
576
- const loadComponent = async (loader) => {
577
- if (!loader) return void 0;
578
- const module = await loader();
579
- return module.default;
580
- };
581
- if (!route) {
582
- const findParentLayouts = async () => {
583
- const segments = currentPath.split("/").filter(Boolean);
584
- for (let i = segments.length - 1; i >= 0; i--) {
585
- const partialPath = "/" + segments.slice(0, i).join("/");
586
- const { route: parentRoute } = findRoute(routes, partialPath);
587
- if (parentRoute) {
588
- const layouts = await Promise.all((parentRoute.layouts || []).map((l) => loadComponent(l)));
589
- return layouts.filter((l) => !!l);
590
- }
591
- }
592
- return [];
593
- };
594
- findParentLayouts().then((layouts) => {
595
- setComponents((prev) => ({ ...prev, page: NotFound, layouts }));
596
- setParams({});
597
- setIsReady(true);
598
- });
599
- return;
600
- }
601
- setParams(extractedParams);
602
- if (!isHydrating.current) setIsNavigating(true);
603
- const loadRoute = async () => {
604
- try {
605
- const [page, layouts, loading, error, notFound2, template] = await Promise.all([
606
- loadComponent(route.component),
607
- Promise.all((route.layouts || []).map((l) => loadComponent(l))),
608
- loadComponent(route.loading),
609
- loadComponent(route.error),
610
- loadComponent(route.notFound),
611
- loadComponent(route.template)
612
- ]);
613
- if (route.metadata) {
614
- metadataCache.set(route.path, route.metadata);
615
- applyMetadata(route.metadata);
616
- }
617
- startTransition(() => {
618
- setComponents({
619
- page: page || null,
620
- layouts: layouts.filter((l) => !!l),
621
- loading,
622
- error,
623
- notFound: notFound2,
624
- template
625
- });
626
- setIsNavigating(false);
627
- setIsReady(true);
628
- isHydrating.current = false;
629
- });
630
- } catch (err) {
631
- console.error("[Olova] Failed to load route:", err);
632
- routerEvents.emit("routeChangeError", err, currentPath);
633
- setComponents({ page: ErrorPage, layouts: [] });
634
- setIsNavigating(false);
635
- setIsReady(true);
636
- }
637
- };
638
- loadRoute();
639
- }, [currentPath, routes]);
640
- return { currentPath, query, components, params, isNavigating, isReady, isHydrating };
641
- }
642
-
643
- // src/router/Router.tsx
644
- import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
645
- injectStyles();
646
- var ErrorBoundary = class extends React2.Component {
647
- constructor(props) {
648
- super(props);
649
- this.reset = () => this.setState({ hasError: false, error: null });
650
- this.state = { hasError: false, error: null };
651
- }
652
- static getDerivedStateFromError(error) {
653
- return { hasError: true, error };
654
- }
655
- render() {
656
- if (this.state.hasError && this.props.fallback) {
657
- const Fallback = this.props.fallback;
658
- return /* @__PURE__ */ jsx3(Fallback, { error: this.state.error, reset: this.reset });
659
- }
660
- return this.props.children;
661
- }
662
- };
663
- function Router({ routes }) {
664
- const routesRef = useRef3(routes);
665
- routesRef.current = routes;
666
- const {
667
- currentPath,
668
- query,
669
- components,
670
- params,
671
- isNavigating,
672
- isReady
673
- } = useRouterInternal(routes);
674
- useEffect4(() => {
675
- const handleHashChange = () => {
676
- routerEvents.emit("hashChangeStart", window.location.hash);
677
- routerEvents.emit("hashChangeComplete", window.location.hash);
678
- };
679
- window.addEventListener("hashchange", handleHashChange);
680
- return () => window.removeEventListener("hashchange", handleHashChange);
681
- }, []);
682
- const routerContextValue = useMemo(() => ({
683
- pathname: currentPath,
684
- params,
685
- query,
686
- asPath: typeof window !== "undefined" ? currentPath + window.location.search + window.location.hash : currentPath,
687
- push: navigate,
688
- replace,
689
- back: () => typeof window !== "undefined" && window.history.back(),
690
- forward: () => typeof window !== "undefined" && window.history.forward(),
691
- prefetch: (url) => prefetchRoute(routesRef.current, url),
692
- reload: () => typeof window !== "undefined" && window.location.reload(),
693
- isReady,
694
- isNavigating,
695
- isFallback: false,
696
- events: routerEvents
697
- }), [currentPath, params, query, isReady, isNavigating]);
698
- if (!components.page) return null;
699
- const { page: Page, layouts, loading: Loading, error: ErrorComp } = components;
700
- let content = /* @__PURE__ */ jsx3(ErrorBoundary, { fallback: ErrorComp, children: /* @__PURE__ */ jsx3(Page, { params, searchParams: query }) });
701
- for (let i = layouts.length - 1; i >= 0; i--) {
702
- const Layout = layouts[i];
703
- content = /* @__PURE__ */ jsx3(ErrorBoundary, { fallback: ErrorComp, children: /* @__PURE__ */ jsx3(Layout, { children: content, params, searchParams: query }) }, i);
704
- }
705
- return /* @__PURE__ */ jsx3(RouterContext.Provider, { value: routerContextValue, children: /* @__PURE__ */ jsx3(ParamsContext.Provider, { value: params, children: /* @__PURE__ */ jsxs2(Suspense, { fallback: Loading ? /* @__PURE__ */ jsx3(Loading, {}) : /* @__PURE__ */ jsx3(LoadingIndicator, {}), children: [
706
- content,
707
- isNavigating && /* @__PURE__ */ jsx3(NavigationProgress, {})
708
- ] }) }) });
709
- }
710
-
711
- // src/head/Head.tsx
712
- import React4, { useEffect as useEffect5, useContext as useContext2 } from "react";
713
-
714
- // src/head/HeadProvider.tsx
715
- import { createContext as createContext2, useState as useState3 } from "react";
716
- import { jsx as jsx4 } from "react/jsx-runtime";
717
- var HeadContext = createContext2(null);
718
- function HeadProvider({ children }) {
719
- const [state, setState] = useState3({
720
- metas: /* @__PURE__ */ new Map(),
721
- links: /* @__PURE__ */ new Map()
722
- });
723
- const update = (updates) => {
724
- setState((prev) => ({ ...prev, ...updates }));
725
- };
726
- return /* @__PURE__ */ jsx4(HeadContext.Provider, { value: { state, update }, children });
727
- }
728
-
729
- // src/head/Head.tsx
730
- function Head({ children }) {
731
- const context = useContext2(HeadContext);
732
- useEffect5(() => {
733
- React4.Children.forEach(children, (child) => {
734
- if (!React4.isValidElement(child)) return;
735
- const { type, props } = child;
736
- if (type === "title") {
737
- const titleProps = props;
738
- if (typeof titleProps.children === "string") {
739
- document.title = titleProps.children;
740
- context?.update({ title: titleProps.children });
741
- }
742
- }
743
- if (type === "meta") {
744
- const metaProps = props;
745
- const key = metaProps.name || metaProps.property || metaProps.httpEquiv;
746
- if (key) {
747
- let meta = document.querySelector(`meta[name="${key}"], meta[property="${key}"]`);
748
- if (!meta) {
749
- meta = document.createElement("meta");
750
- if (metaProps.name) meta.setAttribute("name", metaProps.name);
751
- if (metaProps.property) meta.setAttribute("property", metaProps.property);
752
- document.head.appendChild(meta);
753
- }
754
- meta.setAttribute("content", metaProps.content);
755
- }
756
- }
757
- if (type === "link") {
758
- const linkProps = props;
759
- const key = linkProps.href;
760
- let link = document.querySelector(`link[href="${key}"]`);
761
- if (!link) {
762
- link = document.createElement("link");
763
- link.setAttribute("rel", linkProps.rel);
764
- link.setAttribute("href", linkProps.href);
765
- if (linkProps.as) link.setAttribute("as", linkProps.as);
766
- document.head.appendChild(link);
767
- }
768
- }
769
- });
770
- return () => {
771
- };
772
- }, [children, context]);
773
- return null;
774
- }
775
-
776
- // src/head/ssr.ts
777
- import React5 from "react";
778
-
779
- // src/data/provider.tsx
780
- import { createContext as createContext3 } from "react";
781
- import { jsx as jsx5 } from "react/jsx-runtime";
782
- var PageDataContext = createContext3(null);
783
- function PageDataProvider({
784
- children,
785
- data
786
- }) {
787
- return /* @__PURE__ */ jsx5(PageDataContext.Provider, { value: data, children });
788
- }
789
-
790
- // src/data/hooks.ts
791
- import { useState as useState4, useEffect as useEffect6, useContext as useContext3 } from "react";
792
- function usePageData() {
793
- const context = useContext3(PageDataContext);
794
- const [data, setData] = useState4(context);
795
- useEffect6(() => {
796
- if (typeof window !== "undefined" && window.__OLOVA_PAGE_DATA__) {
797
- setData(window.__OLOVA_PAGE_DATA__);
798
- }
799
- }, []);
800
- return data;
801
- }
802
-
803
- // src/hydration/serialization.ts
804
- function deserializeProps(props) {
805
- const result = {};
806
- for (const [key, value] of Object.entries(props)) {
807
- if (value && typeof value === "object") {
808
- if (value.__type === "Date") {
809
- result[key] = new Date(value.value);
810
- } else if (value.__type === "Map") {
811
- result[key] = new Map(value.value);
812
- } else if (value.__type === "Set") {
813
- result[key] = new Set(value.value);
814
- } else {
815
- result[key] = deserializeProps(value);
816
- }
817
- } else {
818
- result[key] = value;
819
- }
820
- }
821
- return result;
822
- }
823
- function parseFlightPayload() {
824
- if (typeof window === "undefined") return null;
825
- const flight = window.__FLIGHT__;
826
- return flight || null;
827
- }
828
- function getFlightData(key) {
829
- const payload = parseFlightPayload();
830
- if (!payload) return null;
831
- return payload.data[key] ?? null;
832
- }
833
-
834
- // src/hydration/index.ts
835
- init_scheduler();
836
- var clientScheduler = null;
837
- function getHydrationScheduler() {
838
- if (typeof window === "undefined") return null;
839
- if (!clientScheduler) {
840
- const { HydrationScheduler: HydrationScheduler2 } = (init_scheduler(), __toCommonJS(scheduler_exports));
841
- clientScheduler = new HydrationScheduler2();
842
- }
843
- return clientScheduler;
844
- }
845
- export {
846
- Head,
847
- HeadProvider,
848
- HydrationScheduler,
849
- Link,
850
- PageDataProvider,
851
- Router,
852
- defineConfig,
853
- deserializeProps,
854
- getFlightData,
855
- getHydrationScheduler,
856
- navigate,
857
- notFound,
858
- parseFlightPayload,
859
- permanentRedirect,
860
- redirect,
861
- replace,
862
- usePageData,
863
- useParams,
864
- usePathname,
865
- useRouter,
866
- useSearchParams
867
- };
868
- //# sourceMappingURL=index.js.map