olova 2.0.61 → 2.0.63

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 (80) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/README.md +42 -61
  3. package/dist/compiler.d.ts +44 -0
  4. package/dist/compiler.js +2139 -0
  5. package/dist/compiler.js.map +1 -0
  6. package/dist/core.d.ts +4 -0
  7. package/dist/core.js +859 -0
  8. package/dist/core.js.map +1 -0
  9. package/dist/global.d.ts +15 -0
  10. package/dist/global.js +226 -0
  11. package/dist/global.js.map +1 -0
  12. package/dist/index.d.ts +2 -0
  13. package/dist/index.js +2302 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/runtime.d.ts +89 -0
  16. package/dist/runtime.js +633 -0
  17. package/dist/runtime.js.map +1 -0
  18. package/dist/signals-core-BdfWh1Yt.d.ts +43 -0
  19. package/dist/vite.d.ts +5 -0
  20. package/dist/vite.js +2302 -0
  21. package/dist/vite.js.map +1 -0
  22. package/package.json +83 -65
  23. package/dist/chunk-D7SIC5TC.js +0 -367
  24. package/dist/chunk-D7SIC5TC.js.map +0 -1
  25. package/dist/entry-server.cjs +0 -120
  26. package/dist/entry-server.cjs.map +0 -1
  27. package/dist/entry-server.js +0 -115
  28. package/dist/entry-server.js.map +0 -1
  29. package/dist/entry-worker.cjs +0 -133
  30. package/dist/entry-worker.cjs.map +0 -1
  31. package/dist/entry-worker.js +0 -127
  32. package/dist/entry-worker.js.map +0 -1
  33. package/dist/main.cjs +0 -18
  34. package/dist/main.cjs.map +0 -1
  35. package/dist/main.js +0 -16
  36. package/dist/main.js.map +0 -1
  37. package/dist/olova.cjs +0 -1680
  38. package/dist/olova.cjs.map +0 -1
  39. package/dist/olova.d.cts +0 -72
  40. package/dist/olova.d.ts +0 -72
  41. package/dist/olova.js +0 -1321
  42. package/dist/olova.js.map +0 -1
  43. package/dist/performance.cjs +0 -386
  44. package/dist/performance.cjs.map +0 -1
  45. package/dist/performance.js +0 -3
  46. package/dist/performance.js.map +0 -1
  47. package/dist/router.cjs +0 -646
  48. package/dist/router.cjs.map +0 -1
  49. package/dist/router.d.cts +0 -113
  50. package/dist/router.d.ts +0 -113
  51. package/dist/router.js +0 -632
  52. package/dist/router.js.map +0 -1
  53. package/main.tsx +0 -76
  54. package/olova.ts +0 -619
  55. package/src/entry-server.tsx +0 -165
  56. package/src/entry-worker.tsx +0 -201
  57. package/src/generator/index.ts +0 -409
  58. package/src/hydration/flight.ts +0 -320
  59. package/src/hydration/index.ts +0 -12
  60. package/src/hydration/types.ts +0 -225
  61. package/src/logger.ts +0 -182
  62. package/src/main.tsx +0 -24
  63. package/src/performance.ts +0 -488
  64. package/src/plugin/index.ts +0 -204
  65. package/src/router/ErrorBoundary.tsx +0 -145
  66. package/src/router/Link.tsx +0 -117
  67. package/src/router/OlovaRouter.tsx +0 -354
  68. package/src/router/Outlet.tsx +0 -8
  69. package/src/router/context.ts +0 -117
  70. package/src/router/index.ts +0 -29
  71. package/src/router/matching.ts +0 -63
  72. package/src/router/router.tsx +0 -23
  73. package/src/router/search-params.ts +0 -29
  74. package/src/scanner/index.ts +0 -114
  75. package/src/types/index.ts +0 -190
  76. package/src/utils/export.ts +0 -85
  77. package/src/utils/index.ts +0 -4
  78. package/src/utils/naming.ts +0 -54
  79. package/src/utils/path.ts +0 -45
  80. package/tsup.config.ts +0 -35
package/dist/olova.js DELETED
@@ -1,1321 +0,0 @@
1
- import { logger_default, generatePreloadHints, generatePreloadTags, createManualChunks, olovaPerformance } from './chunk-D7SIC5TC.js';
2
- import fs3, { existsSync } from 'fs';
3
- import fs4 from 'fs/promises';
4
- import path4 from 'path';
5
- import { pathToFileURL, fileURLToPath } from 'url';
6
- import mdx from '@mdx-js/rollup';
7
- import { build } from 'vite';
8
-
9
- function extractBalancedBraces(content, startIndex) {
10
- if (content[startIndex] !== "{") return null;
11
- let depth = 0;
12
- for (let i = startIndex; i < content.length; i++) {
13
- if (content[i] === "{") depth++;
14
- else if (content[i] === "}") {
15
- depth--;
16
- if (depth === 0) return content.substring(startIndex, i + 1);
17
- }
18
- }
19
- return null;
20
- }
21
- function extractMetadataSource(content) {
22
- const match = content.match(/export\s+(?:const|let|var)\s+metadata\s*(?::\s*\w+\s*)?=\s*/);
23
- if (!match || match.index === void 0) return void 0;
24
- const afterEquals = match.index + match[0].length;
25
- return extractBalancedBraces(content, afterEquals) || void 0;
26
- }
27
- function detectExportType(filePath) {
28
- try {
29
- const content = fs3.readFileSync(filePath, "utf-8");
30
- const hasMetadata = /export\s+(?:const|let|var)\s+metadata\s*/.test(content) || /export\s*\{\s*metadata\s*\}/.test(content);
31
- const hasRoute = /export\s+(?:const|let|var)\s+route\s*=/.test(content) || /export\s*\{\s*route\s*\}/.test(content);
32
- const hasGetStaticPaths = /export\s+(?:async\s+)?function\s+getStaticPaths/.test(content) || /export\s+(?:const|let|var)\s+getStaticPaths\s*=/.test(content) || /export\s*\{[^}]*getStaticPaths[^}]*\}/.test(content);
33
- const hasLoader = /export\s+(?:async\s+)?function\s+loader/.test(content) || /export\s+(?:const|let|var)\s+loader\s*=/.test(content) || /export\s*\{[^}]*loader[^}]*\}/.test(content);
34
- const metadataSource = extractMetadataSource(content);
35
- if (filePath.toLowerCase().endsWith(".mdx")) {
36
- return {
37
- hasDefault: true,
38
- namedExport: null,
39
- hasMetadata: !!metadataSource,
40
- hasRoute: false,
41
- hasGetStaticPaths: false,
42
- hasLoader: false,
43
- metadataSource
44
- };
45
- }
46
- if (/export\s+default\s+/.test(content)) {
47
- return { hasDefault: true, namedExport: null, hasMetadata, metadataSource, hasRoute, hasGetStaticPaths, hasLoader };
48
- }
49
- const namedMatch = content.match(/export\s+(?:const|function|class)\s+(\w+)/);
50
- if (namedMatch) {
51
- return { hasDefault: false, namedExport: namedMatch[1], hasMetadata, metadataSource, hasRoute, hasGetStaticPaths, hasLoader };
52
- }
53
- const exportMatch = content.match(/export\s*\{\s*(\w+)(?:\s+as\s+default)?\s*\}/);
54
- if (exportMatch) {
55
- if (content.includes("as default")) {
56
- return { hasDefault: true, namedExport: null, hasMetadata, metadataSource, hasRoute, hasGetStaticPaths, hasLoader };
57
- }
58
- return { hasDefault: false, namedExport: exportMatch[1], hasMetadata, metadataSource, hasRoute, hasGetStaticPaths, hasLoader };
59
- }
60
- return { hasDefault: false, namedExport: null, hasMetadata, metadataSource, hasRoute, hasGetStaticPaths, hasLoader };
61
- } catch {
62
- return { hasDefault: true, namedExport: null, hasMetadata: false, hasRoute: false, hasGetStaticPaths: false, hasLoader: false };
63
- }
64
- }
65
-
66
- // src/utils/naming.ts
67
- function getRouteName(path5) {
68
- if (path5 === "/" || path5 === "") return "Root";
69
- const segments = path5.replace(/^\/|\/$/g, "").split(/[\\/]/);
70
- const nameSegments = segments.map((segment) => {
71
- let cleanSegment = segment;
72
- if (cleanSegment.startsWith(":")) {
73
- cleanSegment = cleanSegment.slice(1);
74
- }
75
- if (cleanSegment.startsWith("[") && cleanSegment.endsWith("]")) {
76
- cleanSegment = cleanSegment.slice(1, -1);
77
- if (cleanSegment.startsWith("...")) {
78
- cleanSegment = cleanSegment.slice(3);
79
- }
80
- }
81
- if (cleanSegment === "*") {
82
- return "All";
83
- }
84
- if (cleanSegment.toLowerCase() === "index") {
85
- return "";
86
- }
87
- if (cleanSegment.startsWith("(") && cleanSegment.endsWith(")")) {
88
- cleanSegment = cleanSegment.slice(1, -1);
89
- }
90
- return capitalize(cleanSegment);
91
- });
92
- const finalName = nameSegments.join("");
93
- if (/^\d/.test(finalName)) {
94
- return "Page" + finalName;
95
- }
96
- return finalName;
97
- }
98
- function capitalize(str) {
99
- return str.charAt(0).toUpperCase() + str.slice(1).replace(/[^a-zA-Z0-9]/g, "");
100
- }
101
-
102
- // src/utils/path.ts
103
- function parseDynamicSegment(segment) {
104
- if (segment.match(/^\[\.\.\.(.+)\]$/)) {
105
- const paramName = segment.match(/^\[\.\.\.(.+)\]$/)?.[1] || "slug";
106
- return { isDynamic: true, paramName, isCatchAll: true };
107
- }
108
- const bracketMatch = segment.match(/^\[(.+)\]$/);
109
- if (bracketMatch) {
110
- return { isDynamic: true, paramName: bracketMatch[1], isCatchAll: false };
111
- }
112
- return { isDynamic: false, paramName: null, isCatchAll: false };
113
- }
114
- function isRouteGroup(segment) {
115
- return /^\(.+\)$/.test(segment);
116
- }
117
- function pathToRoute(relativePath, sep) {
118
- const params = [];
119
- let hasCatchAll = false;
120
- const segments = relativePath.split(sep).filter(Boolean);
121
- const routeSegments = segments.filter((segment) => !isRouteGroup(segment)).map((segment) => {
122
- const { isDynamic, paramName, isCatchAll } = parseDynamicSegment(segment);
123
- if (isDynamic && paramName) {
124
- params.push(paramName);
125
- if (isCatchAll) {
126
- hasCatchAll = true;
127
- return `*`;
128
- }
129
- return `:${paramName}`;
130
- }
131
- return segment;
132
- });
133
- const routePath = "/" + routeSegments.join("/");
134
- return { routePath: routePath === "/" ? "/" : routePath, params, hasCatchAll };
135
- }
136
-
137
- // src/generator/index.ts
138
- function stripImportExtension(filePath) {
139
- return filePath.replace(/\.tsx?$/, "");
140
- }
141
- function pathToRouteId(routePath) {
142
- if (routePath === "/") return "_root";
143
- return routePath.replace(/^\//, "").replace(/\//g, "_").replace(/:/g, "$").replace(/\*/g, "$catchall");
144
- }
145
- function extractParamNames(routePath) {
146
- const params = [];
147
- for (const segment of routePath.split("/")) {
148
- if (segment.startsWith(":")) params.push(segment.slice(1));
149
- else if (segment === "*") params.push("*");
150
- }
151
- return params;
152
- }
153
- function generateParamType(routePath) {
154
- const params = extractParamNames(routePath);
155
- if (params.length === 0) return null;
156
- const fields = params.map(
157
- (p) => p === "*" ? "'*': string" : `${p}: string`
158
- );
159
- return `{ ${fields.join("; ")} }`;
160
- }
161
- function generateRouteTree(routes, notFoundPages, layouts, loadingPages, errorPages, middlewares, srcDir, packageName = "olovastart") {
162
- const usedNames = /* @__PURE__ */ new Map();
163
- const getUniqueName = (baseName) => {
164
- const count = usedNames.get(baseName) || 0;
165
- usedNames.set(baseName, count + 1);
166
- return count === 0 ? baseName : `${baseName}${count}`;
167
- };
168
- const getRouteName2 = (filePath, suffix = "") => {
169
- const relPath = path4.relative(srcDir, filePath);
170
- const pathNoExt = relPath.replace(/\.(tsx?|mdx)$/, "");
171
- if (pathNoExt === "" || pathNoExt === "." || pathNoExt === "index") {
172
- return getUniqueName("Root" + suffix);
173
- }
174
- const name = getRouteName(pathNoExt);
175
- if (!name) return getUniqueName("Root" + suffix);
176
- if (suffix && name.toLowerCase().endsWith(suffix.toLowerCase())) {
177
- return getUniqueName(name);
178
- }
179
- return getUniqueName(name + suffix);
180
- };
181
- const findClosestEntry = (routePath, entries) => {
182
- let best = null;
183
- for (const entry of entries) {
184
- const ep = entry.path;
185
- if (ep === "/" || routePath === ep || routePath.startsWith(ep + "/")) {
186
- if (!best || ep.length > best.path.length) best = entry;
187
- }
188
- }
189
- return best;
190
- };
191
- const routeNames = [];
192
- const routeImports = routes.map((route) => {
193
- const relativePath = stripImportExtension("./app/" + path4.relative(srcDir, route.component).replace(/\\/g, "/"));
194
- const moduleName = getRouteName2(route.component, "RouteModule");
195
- const lazyName = getRouteName2(route.component, "Route");
196
- routeNames.push({ moduleName, lazyName, eager: true });
197
- return `import * as ${moduleName} from '${relativePath}';`;
198
- }).join("\n");
199
- const notFoundNames = [];
200
- const notFoundImports = notFoundPages.map((nf) => {
201
- const relativePath = stripImportExtension("./app/" + path4.relative(srcDir, nf.filePath).replace(/\\/g, "/"));
202
- if (nf.hasMetadata) {
203
- const moduleName = getRouteName2(nf.filePath, "NotFoundModule");
204
- notFoundNames.push(moduleName);
205
- return `import * as ${moduleName} from '${relativePath}';`;
206
- }
207
- const importName = getRouteName2(nf.filePath, "NotFound");
208
- notFoundNames.push(importName);
209
- if (nf.hasDefault) return `import ${importName} from '${relativePath}';`;
210
- if (nf.namedExport) return `import { ${nf.namedExport} as ${importName} } from '${relativePath}';`;
211
- return `import ${importName} from '${relativePath}';`;
212
- }).join("\n");
213
- const layoutNames = [];
214
- const layoutImports = layouts.map((layout) => {
215
- const relativePath = stripImportExtension("./app/" + path4.relative(srcDir, layout.filePath).replace(/\\/g, "/"));
216
- if (layout.hasMetadata) {
217
- const moduleName = getRouteName2(layout.filePath, "LayoutModule");
218
- layoutNames.push(moduleName);
219
- return `import * as ${moduleName} from '${relativePath}';`;
220
- }
221
- const importName = getRouteName2(layout.filePath, "Layout");
222
- layoutNames.push(importName);
223
- if (layout.hasDefault) return `import ${importName} from '${relativePath}';`;
224
- if (layout.namedExport) return `import { ${layout.namedExport} as ${importName} } from '${relativePath}';`;
225
- return `import ${importName} from '${relativePath}';`;
226
- }).join("\n");
227
- const loadingNames = [];
228
- const loadingImports = loadingPages.map((lp) => {
229
- const relativePath = stripImportExtension("./app/" + path4.relative(srcDir, lp.filePath).replace(/\\/g, "/"));
230
- const importName = getRouteName2(lp.filePath, "Loading");
231
- loadingNames.push(importName);
232
- if (lp.hasDefault) return `import ${importName} from '${relativePath}';`;
233
- if (lp.namedExport) return `import { ${lp.namedExport} as ${importName} } from '${relativePath}';`;
234
- return `import ${importName} from '${relativePath}';`;
235
- }).join("\n");
236
- const errorNames = [];
237
- const errorImports = errorPages.map((ep) => {
238
- const relativePath = stripImportExtension("./app/" + path4.relative(srcDir, ep.filePath).replace(/\\/g, "/"));
239
- const importName = getRouteName2(ep.filePath, "Error");
240
- errorNames.push(importName);
241
- if (ep.hasDefault) return `import ${importName} from '${relativePath}';`;
242
- if (ep.namedExport) return `import { ${ep.namedExport} as ${importName} } from '${relativePath}';`;
243
- return `import ${importName} from '${relativePath}';`;
244
- }).join("\n");
245
- const middlewareNames = [];
246
- const middlewareImports = middlewares.map((mw) => {
247
- const relativePath = stripImportExtension("./app/" + path4.relative(srcDir, mw.filePath).replace(/\\/g, "/"));
248
- const importName = getRouteName2(mw.filePath, "Middleware");
249
- middlewareNames.push(importName);
250
- if (mw.hasDefault) return `import ${importName} from '${relativePath}';`;
251
- if (mw.namedExport) return `import { ${mw.namedExport} as ${importName} } from '${relativePath}';`;
252
- return `import ${importName} from '${relativePath}';`;
253
- }).join("\n");
254
- const routeObjects = routes.map((route, index) => {
255
- const { moduleName, lazyName, eager } = routeNames[index];
256
- const component = eager ? `${moduleName}.default` : lazyName;
257
- const routeId = pathToRouteId(route.path);
258
- const fields = [
259
- `id: '${routeId}'`,
260
- `path: '${route.path}'`,
261
- `component: ${component}`
262
- ];
263
- if (route.params && route.params.length > 0) {
264
- fields.push(`params: [${route.params.map((p) => `'${p}'`).join(", ")}]`);
265
- }
266
- if (route.metadataSource) {
267
- fields.push(`metadata: ${route.metadataSource}`);
268
- } else if (route.hasMetadata) {
269
- fields.push(`metadata: ${moduleName}.metadata`);
270
- }
271
- if (route.hasGetStaticPaths) {
272
- fields.push(`getStaticPaths: ${moduleName}.getStaticPaths`);
273
- }
274
- if (route.hasLoader) {
275
- fields.push(`loader: ${moduleName}.loader`);
276
- }
277
- const closestLoading = findClosestEntry(route.path, loadingPages);
278
- if (closestLoading) {
279
- const idx = loadingPages.indexOf(closestLoading);
280
- fields.push(`loading: ${loadingNames[idx]}`);
281
- }
282
- const closestError = findClosestEntry(route.path, errorPages);
283
- if (closestError) {
284
- const idx = errorPages.indexOf(closestError);
285
- fields.push(`error: ${errorNames[idx]}`);
286
- }
287
- return ` {
288
- ${fields.join(",\n ")}
289
- }`;
290
- }).join(",\n");
291
- const notFoundObjects = notFoundPages.map((nf, index) => {
292
- const name = notFoundNames[index];
293
- const isModule = nf.hasMetadata;
294
- const component = isModule ? `${name}.default` : name;
295
- const fields = [
296
- `pathPrefix: '${nf.pathPrefix}'`,
297
- `component: ${component}`
298
- ];
299
- if (isModule) fields.push(`metadata: ${name}.metadata`);
300
- return ` {
301
- ${fields.join(",\n ")}
302
- }`;
303
- }).join(",\n");
304
- const layoutObjects = layouts.map((layout, index) => {
305
- const name = layoutNames[index];
306
- const isModule = layout.hasMetadata;
307
- const component = isModule ? `${name}.default` : name;
308
- const childRouteIds = routes.filter((r) => {
309
- if (layout.path === "/") return true;
310
- return r.path === layout.path || r.path.startsWith(layout.path + "/");
311
- }).map((r) => `'${pathToRouteId(r.path)}'`);
312
- const fields = [
313
- `path: '${layout.path}'`,
314
- `layout: ${component}`,
315
- `children: [${childRouteIds.join(", ")}]`
316
- ];
317
- if (isModule) fields.push(`metadata: ${name}.metadata`);
318
- return ` {
319
- ${fields.join(",\n ")}
320
- }`;
321
- }).join(",\n");
322
- const middlewareEntries = middlewares.map((mw, index) => {
323
- return ` '${mw.path}': ${middlewareNames[index]}`;
324
- }).join(",\n");
325
- const routeParamTypes = routes.filter((r) => r.params && r.params.length > 0).map((r) => {
326
- const paramType = generateParamType(r.path);
327
- return paramType ? ` '${r.path}': ${paramType};` : null;
328
- }).filter(Boolean).join("\n");
329
- const routePaths = routes.length > 0 ? routes.map((r) => `'${r.path}'`).join(" | ") : "never";
330
- const routeIds = routes.length > 0 ? routes.map((r) => `'${pathToRouteId(r.path)}'`).join(" | ") : "never";
331
- const manifestEntries = routes.map((r) => {
332
- const paramNames = extractParamNames(r.path);
333
- const isDynamic = paramNames.length > 0;
334
- const hasCatchAll = r.path.includes("*");
335
- return ` '${pathToRouteId(r.path)}': { id: '${pathToRouteId(r.path)}', path: '${r.path}', params: [${paramNames.map((p) => `'${p}'`).join(", ")}], isDynamic: ${isDynamic}, hasCatchAll: ${hasCatchAll}, hasMetadata: ${r.hasMetadata}, hasGetStaticPaths: ${r.hasGetStaticPaths} }`;
336
- }).join(",\n");
337
- const allImports = [
338
- routeImports,
339
- notFoundImports,
340
- layoutImports,
341
- loadingImports,
342
- errorImports,
343
- middlewareImports
344
- ].filter(Boolean).join("\n");
345
- return `/* prettier-ignore-start */
346
-
347
- /* eslint-disable */
348
-
349
- // @ts-nocheck
350
-
351
- // noinspection JSUnusedGlobalSymbols
352
-
353
- /**
354
- * This file was automatically generated by Olova Router.
355
- * DO NOT MODIFY IT BY HAND. Instead, modify the source route files
356
- * and regenerate this file by running the dev server or build command.
357
- *
358
- * @generated Olova Route Tree
359
- * @routes ${routes.length}
360
- * @layouts ${layouts.length}
361
- * @404s ${notFoundPages.length}
362
- * @loading ${loadingPages.length}
363
- * @errors ${errorPages.length}
364
- * @middleware ${middlewares.length}
365
- */
366
-
367
- import { lazy } from 'react';
368
- import {
369
- createLink,
370
- OlovaRouter,
371
- Outlet,
372
- redirect,
373
- useIsNavigating,
374
- useNavigationEvent,
375
- useParams,
376
- usePathname,
377
- useRedirect,
378
- useRouter,
379
- useSearchParams,
380
- useSelectedLayoutSegment,
381
- useSelectedLayoutSegments,
382
- } from '${packageName}/router';
383
- ${allImports}
384
-
385
- /* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
386
- * Route Definitions
387
- * \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
388
-
389
- export const routes = [
390
- ${routeObjects}
391
- ];
392
-
393
- export const notFoundPages = [
394
- ${notFoundObjects}
395
- ];
396
-
397
- export const layouts = [
398
- ${layoutObjects}
399
- ];
400
- ${middlewares.length > 0 ? `
401
- export const middlewares = {
402
- ${middlewareEntries}
403
- };
404
- ` : `
405
- export const middlewares = {};
406
- `}
407
- /* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
408
- * Route Manifest (build-time introspection, sitemap generation, etc.)
409
- * \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
410
-
411
- export const routeManifest = {
412
- ${manifestEntries}
413
- } as const;
414
-
415
- /* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
416
- * Type-Safe Route Types
417
- * \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
418
-
419
- export type RoutePaths = ${routePaths};
420
-
421
- export type RouteIds = ${routeIds};
422
-
423
- export interface RouteParams {
424
- ${routeParamTypes || " // No dynamic routes"}
425
- }
426
-
427
- export type ParamsFor<P extends RoutePaths> = P extends keyof RouteParams ? RouteParams[P] : Record<string, never>;
428
-
429
- /* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
430
- * Route Lookup Helpers
431
- * \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
432
-
433
- const _routeById = new Map(routes.map(r => [r.id, r]));
434
-
435
- export function getRouteById(id: RouteIds) {
436
- return _routeById.get(id);
437
- }
438
-
439
- export function getRouteByPath(path: RoutePaths) {
440
- return routes.find(r => r.path === path);
441
- }
442
-
443
- /* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
444
- * Exports
445
- * \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
446
-
447
- export const Link = createLink<RoutePaths>();
448
-
449
- export {
450
- OlovaRouter,
451
- Outlet,
452
- redirect,
453
- useIsNavigating,
454
- useNavigationEvent,
455
- useParams,
456
- usePathname,
457
- useRedirect,
458
- useRouter,
459
- useSearchParams,
460
- useSelectedLayoutSegment,
461
- useSelectedLayoutSegments,
462
- };
463
-
464
- export type {
465
- NavigateOptions,
466
- NotFoundPageConfig,
467
- SearchParams,
468
- SetSearchParamsOptions,
469
- LayoutRoute,
470
- Metadata,
471
- } from '${packageName}/router';
472
-
473
- /* prettier-ignore-end */
474
- `;
475
- }
476
- var RESERVED_NAMES = /* @__PURE__ */ new Set([
477
- "page",
478
- "layout",
479
- "loading",
480
- "error",
481
- "not-found",
482
- "middleware",
483
- "route.tree"
484
- ]);
485
- function scanDirectory(dir, rootDir, extensions, routes, notFoundPages, layouts, loadingPages, errorPages, middlewares, isRoot = false) {
486
- if (!fs3.existsSync(dir)) return;
487
- const entries = fs3.readdirSync(dir, { withFileTypes: true });
488
- for (const entry of entries) {
489
- const fullPath = path4.join(dir, entry.name);
490
- if (entry.isDirectory()) {
491
- if (entry.name === "node_modules" || entry.name === "assets" || entry.name.startsWith("_")) continue;
492
- scanDirectory(fullPath, rootDir, extensions, routes, notFoundPages, layouts, loadingPages, errorPages, middlewares, false);
493
- } else if (entry.isFile()) {
494
- const ext = path4.extname(entry.name);
495
- const baseName = path4.basename(entry.name, ext);
496
- if (baseName === "layout" && extensions.includes(ext)) {
497
- const relativePath = path4.relative(rootDir, dir);
498
- const { routePath } = pathToRoute(relativePath, path4.sep);
499
- layouts.push({
500
- path: isRoot ? "/" : routePath,
501
- filePath: fullPath
502
- });
503
- } else if (baseName === "loading" && extensions.includes(ext)) {
504
- const relativePath = path4.relative(rootDir, dir);
505
- const { routePath } = pathToRoute(relativePath, path4.sep);
506
- loadingPages.push({
507
- path: isRoot ? "/" : routePath,
508
- filePath: fullPath
509
- });
510
- } else if (baseName === "error" && extensions.includes(ext)) {
511
- const relativePath = path4.relative(rootDir, dir);
512
- const { routePath } = pathToRoute(relativePath, path4.sep);
513
- errorPages.push({
514
- path: isRoot ? "/" : routePath,
515
- filePath: fullPath
516
- });
517
- } else if (baseName === "middleware" && extensions.includes(ext)) {
518
- const relativePath = path4.relative(rootDir, dir);
519
- const { routePath } = pathToRoute(relativePath, path4.sep);
520
- middlewares.push({
521
- path: isRoot ? "/" : routePath,
522
- filePath: fullPath
523
- });
524
- } else if ((baseName === "not-found" || baseName === "404") && extensions.includes(ext)) {
525
- const relativeParts = path4.relative(rootDir, dir).split(path4.sep).filter(Boolean);
526
- const filteredParts = relativeParts.filter((p) => !isRouteGroup(p));
527
- const pathPrefix = isRoot ? "" : "/" + filteredParts.join("/");
528
- notFoundPages.push({ pathPrefix: pathPrefix || "", filePath: fullPath });
529
- } else if (baseName === "page" && extensions.includes(ext)) {
530
- const relativePath = path4.relative(rootDir, dir);
531
- const { routePath, params } = pathToRoute(relativePath, path4.sep);
532
- routes.push({ path: routePath, filePath: fullPath, isDynamic: params.length > 0, params });
533
- } else if (!RESERVED_NAMES.has(baseName) && !baseName.endsWith(".d") && !baseName.startsWith("_") && extensions.includes(ext)) {
534
- const relativePath = path4.relative(rootDir, fullPath);
535
- const relativePathNoExt = relativePath.substring(0, relativePath.length - ext.length);
536
- const { routePath, params } = pathToRoute(relativePathNoExt, path4.sep);
537
- routes.push({ path: routePath, filePath: fullPath, isDynamic: params.length > 0, params });
538
- }
539
- }
540
- }
541
- }
542
- function scanRoutes(rootDir, extensions) {
543
- const routes = [];
544
- const notFoundPages = [];
545
- const layouts = [];
546
- const loadingPages = [];
547
- const errorPages = [];
548
- const middlewares = [];
549
- const absoluteRoot = path4.isAbsolute(rootDir) ? rootDir : path4.resolve(rootDir);
550
- if (!fs3.existsSync(absoluteRoot)) {
551
- throw new Error(`Olova Router: Root directory does not exist: ${absoluteRoot}`);
552
- }
553
- scanDirectory(absoluteRoot, absoluteRoot, extensions, routes, notFoundPages, layouts, loadingPages, errorPages, middlewares, true);
554
- routes.sort((a, b) => a.isDynamic !== b.isDynamic ? a.isDynamic ? 1 : -1 : a.path.localeCompare(b.path));
555
- notFoundPages.sort((a, b) => b.pathPrefix.length - a.pathPrefix.length);
556
- layouts.sort((a, b) => a.path.length - b.path.length);
557
- loadingPages.sort((a, b) => b.path.length - a.path.length);
558
- errorPages.sort((a, b) => b.path.length - a.path.length);
559
- middlewares.sort((a, b) => a.path.length - b.path.length);
560
- return { routes, notFoundPages, layouts, loadingPages, errorPages, middlewares };
561
- }
562
-
563
- // src/plugin/index.ts
564
- function olovaRouter(options = {}) {
565
- const rootDir = options.rootDir || "src/app";
566
- const extensions = options.extensions || [".tsx", ".ts", ".mdx"];
567
- const packageName = options.packageName || "olovastart";
568
- let config;
569
- let absoluteRootDir;
570
- let watcher = null;
571
- let timer = null;
572
- function generateRouteTreeFile() {
573
- const { routes, notFoundPages, layouts, loadingPages, errorPages, middlewares } = scanRoutes(absoluteRootDir, extensions);
574
- const routeConfigs = routes.map((r) => {
575
- let exportInfo = detectExportType(r.filePath);
576
- if (r.filePath.toLowerCase().endsWith(".mdx")) {
577
- exportInfo = {
578
- ...exportInfo,
579
- hasDefault: true,
580
- namedExport: null
581
- };
582
- }
583
- return {
584
- path: r.path,
585
- component: r.filePath.replace(/\\/g, "/"),
586
- params: r.params.length > 0 ? r.params : void 0,
587
- hasDefault: exportInfo.hasDefault,
588
- namedExport: exportInfo.namedExport,
589
- hasMetadata: exportInfo.hasMetadata,
590
- metadataSource: exportInfo.metadataSource,
591
- hasRoute: exportInfo.hasRoute,
592
- hasGetStaticPaths: exportInfo.hasGetStaticPaths,
593
- hasLoader: exportInfo.hasLoader
594
- };
595
- });
596
- const notFoundConfigs = notFoundPages.map((nf) => {
597
- const exportInfo = detectExportType(nf.filePath);
598
- return {
599
- pathPrefix: nf.pathPrefix,
600
- filePath: nf.filePath.replace(/\\/g, "/"),
601
- hasDefault: exportInfo.hasDefault,
602
- namedExport: exportInfo.namedExport,
603
- hasMetadata: exportInfo.hasMetadata
604
- };
605
- });
606
- const layoutConfigs = layouts.map((l) => {
607
- const exportInfo = detectExportType(l.filePath);
608
- return {
609
- path: l.path,
610
- filePath: l.filePath.replace(/\\/g, "/"),
611
- hasDefault: exportInfo.hasDefault,
612
- namedExport: exportInfo.namedExport,
613
- hasMetadata: exportInfo.hasMetadata
614
- };
615
- });
616
- const loadingConfigs = loadingPages.map((lp) => {
617
- const exportInfo = detectExportType(lp.filePath);
618
- return {
619
- path: lp.path,
620
- filePath: lp.filePath.replace(/\\/g, "/"),
621
- hasDefault: exportInfo.hasDefault,
622
- namedExport: exportInfo.namedExport
623
- };
624
- });
625
- const errorConfigs = errorPages.map((ep) => {
626
- const exportInfo = detectExportType(ep.filePath);
627
- return {
628
- path: ep.path,
629
- filePath: ep.filePath.replace(/\\/g, "/"),
630
- hasDefault: exportInfo.hasDefault,
631
- namedExport: exportInfo.namedExport
632
- };
633
- });
634
- const middlewareConfigs = middlewares.map((mw) => {
635
- const exportInfo = detectExportType(mw.filePath);
636
- return {
637
- path: mw.path,
638
- filePath: mw.filePath.replace(/\\/g, "/"),
639
- hasDefault: exportInfo.hasDefault,
640
- namedExport: exportInfo.namedExport
641
- };
642
- });
643
- const content = generateRouteTree(routeConfigs, notFoundConfigs, layoutConfigs, loadingConfigs, errorConfigs, middlewareConfigs, absoluteRootDir, packageName);
644
- const treePath = path4.resolve(config.root, "src", "route.tree.ts");
645
- const existing = fs3.existsSync(treePath) ? fs3.readFileSync(treePath, "utf-8") : "";
646
- if (existing !== content) {
647
- fs3.writeFileSync(treePath, content);
648
- console.log("\x1B[32m[olova]\x1B[0m Route tree updated");
649
- }
650
- }
651
- function startWatcher() {
652
- if (watcher) return;
653
- watcher = fs3.watch(absoluteRootDir, { recursive: true }, (eventType, filename) => {
654
- if (!filename) return;
655
- if (filename.includes("route.tree.ts")) return;
656
- const ext = path4.extname(filename);
657
- const isConfiguredExtension = extensions.includes(ext);
658
- const isPageFile = filename.includes("page") && isConfiguredExtension;
659
- const is404File = filename.includes("404") && isConfiguredExtension;
660
- const isNotFoundFile = filename.includes("not-found") && isConfiguredExtension;
661
- const isLayoutFile = filename.includes("layout") && isConfiguredExtension;
662
- const isLoadingFile = filename.includes("loading") && isConfiguredExtension;
663
- const isErrorFile = filename.includes("error") && isConfiguredExtension;
664
- const isMiddlewareFile = filename.includes("middleware") && isConfiguredExtension;
665
- const isDirectory = !filename.includes(".");
666
- const isDynamicSegment = filename.includes("[");
667
- const isRenameEvent = eventType === "rename";
668
- if (isPageFile || is404File || isNotFoundFile || isLayoutFile || isLoadingFile || isErrorFile || isMiddlewareFile || isDirectory || isDynamicSegment || isRenameEvent) {
669
- if (isPageFile && filename) {
670
- const fullPath = path4.join(absoluteRootDir, filename);
671
- if (fs3.existsSync(fullPath)) {
672
- const stat = fs3.statSync(fullPath);
673
- if (stat.size === 0 && !filename.endsWith(".mdx")) {
674
- const relativeDir = path4.relative(absoluteRootDir, path4.dirname(fullPath));
675
- const pascalCaseName = getRouteName(relativeDir);
676
- const boilerplate = `
677
- export const metadata = {
678
- title: "${pascalCaseName}",
679
- description: "${pascalCaseName} page",
680
- }
681
-
682
- export default function ${pascalCaseName}() {
683
- return (
684
- <div>
685
- <h1>${pascalCaseName}</h1>
686
- </div>
687
- );
688
- }
689
- `;
690
- fs3.writeFileSync(fullPath, boilerplate);
691
- console.log(`\x1B[32m[olova]\x1B[0m Generated boilerplate for ${filename}`);
692
- }
693
- }
694
- }
695
- if (timer) clearTimeout(timer);
696
- timer = setTimeout(() => {
697
- try {
698
- generateRouteTreeFile();
699
- } catch (error) {
700
- console.error("\x1B[31m[olova]\x1B[0m Error generating route tree:", error);
701
- }
702
- }, 100);
703
- }
704
- });
705
- console.log("\x1B[32m[olova]\x1B[0m Watching for route changes...");
706
- }
707
- const routerPlugin = {
708
- name: "olova-router",
709
- configResolved(resolvedConfig) {
710
- config = resolvedConfig;
711
- absoluteRootDir = path4.resolve(config.root, rootDir);
712
- },
713
- buildStart() {
714
- generateRouteTreeFile();
715
- if (config.command === "serve") {
716
- startWatcher();
717
- }
718
- },
719
- buildEnd() {
720
- if (watcher) {
721
- watcher.close();
722
- watcher = null;
723
- }
724
- }
725
- };
726
- return [
727
- { enforce: "pre", ...mdx() },
728
- routerPlugin
729
- ];
730
- }
731
-
732
- // src/hydration/flight.ts
733
- function safeStringify(data) {
734
- return JSON.stringify(data).replace(/[<>\/\u2028\u2029]/g, (char) => {
735
- switch (char) {
736
- case "<":
737
- return "\\u003c";
738
- case ">":
739
- return "\\u003e";
740
- case "/":
741
- return "\\u002f";
742
- case "\u2028":
743
- return "\\u2028";
744
- case "\u2029":
745
- return "\\u2029";
746
- default:
747
- return char;
748
- }
749
- });
750
- }
751
- function generatePageName(route) {
752
- if (route === "/") return "HomePage";
753
- return route.slice(1).split("/").filter(Boolean).map((s) => s.charAt(0).toUpperCase() + s.slice(1).replace(/[^a-zA-Z0-9]/g, "")).join("") + "Page";
754
- }
755
- function generatePattern(route) {
756
- if (route === "/") return "/";
757
- return route.replace(/\[\.\.\.([^\]]+)\]/g, "*").replace(/\[([^\]]+)\]/g, ":$1");
758
- }
759
- function generateFlightRuntime() {
760
- return `<script>
761
- (function(){
762
- var f=self.__olova_f||[];
763
- var p={R:'$route',M:'$meta',T:'$tree',D:'$schema',A:'$assets',H:'$hints',S:'$state',L:'$loader',Q:'$query'};
764
- var g=self.$OLOVA={};
765
- function h(c){var t=c[1],d=c[2];if(t==='E')return;var k=p[t];if(k){if(t==='L'||t==='Q'){g[k]={data:d,timestamp:c[3]||Date.now()}}else{g[k]=d}}}
766
- for(var i=0;i<f.length;i++)h(f[i]);
767
- self.__olova_f={push:h,length:0};
768
- })();
769
- </script>`;
770
- }
771
- function generateOlovaHydration(data, buildId) {
772
- const meta = data.metadata || {};
773
- const chunks = [];
774
- const routeData = {
775
- path: data.route,
776
- params: data.params || {},
777
- pattern: generatePattern(data.route),
778
- isStatic: data.isStatic ?? true,
779
- buildId
780
- };
781
- chunks.push(`<script>(self.__olova_f=self.__olova_f||[]).push([0,"R",${safeStringify(routeData)}])</script>`);
782
- const metadataData = {
783
- title: meta.title || "Olova App",
784
- description: meta.description || "",
785
- keywords: Array.isArray(meta.keywords) ? meta.keywords : [],
786
- robots: meta.robots || "index, follow",
787
- canonical: meta.canonical || null,
788
- og: {
789
- type: "website",
790
- locale: "en_US",
791
- ...meta.openGraph || {}
792
- },
793
- twitter: {
794
- card: "summary_large_image",
795
- ...meta.twitter || {}
796
- }
797
- };
798
- chunks.push(`<script>(self.__olova_f).push([1,"M",${safeStringify(metadataData)}])</script>`);
799
- const treeData = {
800
- layout: "RootLayout",
801
- page: generatePageName(data.route),
802
- template: null,
803
- loading: null,
804
- error: null,
805
- notFound: null
806
- };
807
- chunks.push(`<script>(self.__olova_f).push([2,"T",${safeStringify(treeData)}])</script>`);
808
- const assetsData = {
809
- chunks: data.chunks || [],
810
- styles: [],
811
- prefetch: (data.chunks || []).slice(0, 5)
812
- };
813
- chunks.push(`<script>(self.__olova_f).push([3,"A",${safeStringify(assetsData)}])</script>`);
814
- const hintsData = {
815
- dnsPrefetch: ["fonts.googleapis.com", "fonts.gstatic.com"],
816
- preconnect: ["https://fonts.googleapis.com", "https://fonts.gstatic.com"],
817
- modulePreload: (data.chunks || []).slice(0, 3)
818
- };
819
- chunks.push(`<script>(self.__olova_f).push([4,"H",${safeStringify(hintsData)}])</script>`);
820
- const stateData = {
821
- hydrated: false,
822
- streaming: false,
823
- ready: true,
824
- timestamp: Date.now(),
825
- version: "1.0.0",
826
- buildId
827
- };
828
- chunks.push(`<script>(self.__olova_f).push([5,"S",${safeStringify(stateData)}])</script>`);
829
- if (data.loaderData) {
830
- chunks.push(`<script>(self.__olova_f).push([6,"L",${safeStringify(data.loaderData)},${Date.now()}])</script>`);
831
- }
832
- if (data.queryState) {
833
- chunks.push(`<script>(self.__olova_f).push([7,"Q",${safeStringify(data.queryState)},${Date.now()}])</script>`);
834
- }
835
- chunks.push(`<script>(self.__olova_f).push([8,"E",null])</script>`);
836
- chunks.push(generateFlightRuntime());
837
- return chunks.join("");
838
- }
839
- function generateJsonLd(data) {
840
- const meta = data.metadata || {};
841
- const jsonLd = {
842
- "@context": "https://schema.org",
843
- "@graph": [
844
- {
845
- "@type": "WebPage",
846
- "@id": data.route,
847
- name: meta.title || "Olova App",
848
- description: meta.description || "",
849
- url: data.route,
850
- isPartOf: {
851
- "@type": "WebSite",
852
- name: "Olova App"
853
- }
854
- },
855
- {
856
- "@type": "BreadcrumbList",
857
- itemListElement: data.route.split("/").filter(Boolean).map((segment, index, arr) => ({
858
- "@type": "ListItem",
859
- position: index + 1,
860
- name: segment.charAt(0).toUpperCase() + segment.slice(1),
861
- item: "/" + arr.slice(0, index + 1).join("/")
862
- }))
863
- }
864
- ]
865
- };
866
- if (meta.schema) {
867
- const graph = jsonLd["@graph"];
868
- if (Array.isArray(meta.schema)) {
869
- graph.push(...meta.schema);
870
- } else {
871
- graph.push(meta.schema);
872
- }
873
- }
874
- return `<script type="application/ld+json">${JSON.stringify(jsonLd)}</script>`;
875
- }
876
- function generateResourceHints(data) {
877
- const hints = [];
878
- hints.push(`<link rel="dns-prefetch" href="//fonts.googleapis.com">`);
879
- hints.push(`<link rel="dns-prefetch" href="//fonts.gstatic.com">`);
880
- hints.push(`<link rel="preconnect" href="https://fonts.googleapis.com" crossorigin>`);
881
- hints.push(`<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>`);
882
- if (data.chunks) {
883
- for (const chunk of data.chunks.slice(0, 3)) {
884
- hints.push(`<link rel="modulepreload" href="/${chunk}">`);
885
- }
886
- }
887
- return hints.join("");
888
- }
889
- function generateBuildId() {
890
- const timestamp = Date.now().toString(36);
891
- const random = Math.random().toString(36).substring(2, 8);
892
- return `${timestamp}-${random}`;
893
- }
894
-
895
- // olova.ts
896
- function olova(options = {}) {
897
- const virtualModuleId = "virtual:olova-entry";
898
- const serverVirtualModuleId = "virtual:olova-server-entry";
899
- const workerVirtualModuleId = "virtual:olova-worker-entry";
900
- const appVirtualModuleId = "virtual:olova-app";
901
- const resolvedAppVirtualModuleId = "\0" + appVirtualModuleId;
902
- let config;
903
- const htmlContent = `<!--app-html-->`;
904
- const minifyHtml = (html) => {
905
- return html.replace(/>\s+</g, "><").replace(/\s{2,}/g, " ").replace(/<!--[\s\S]*?-->/g, "").trim();
906
- };
907
- return [
908
- olovaRouter({ packageName: options.packageName }),
909
- {
910
- name: "vite-plugin-olova",
911
- config(userConfig, { isSsrBuild }) {
912
- const perfEnabled = options.performance?.enabled !== false;
913
- if (isSsrBuild) {
914
- return {
915
- build: {
916
- rollupOptions: {
917
- input: userConfig.build?.rollupOptions?.input || virtualModuleId,
918
- output: {
919
- // Simple output for SSR
920
- entryFileNames: "index.js",
921
- chunkFileNames: "[name].js",
922
- assetFileNames: "[name].[ext]",
923
- format: "esm",
924
- manualChunks: void 0
925
- }
926
- }
927
- }
928
- };
929
- }
930
- const buildConfig = {
931
- // Output directory for assets
932
- assetsDir: "assets/_olova",
933
- // Report compressed size in build output
934
- reportCompressedSize: true,
935
- rollupOptions: {
936
- input: userConfig.build?.rollupOptions?.input || virtualModuleId,
937
- output: {
938
- // Optimized chunk naming for caching
939
- chunkFileNames: "assets/_olova/[name]-[hash].js",
940
- entryFileNames: "assets/_olova/[name]-[hash].js",
941
- assetFileNames: "assets/_olova/[name]-[hash].[ext]",
942
- // Enable smart code splitting
943
- ...perfEnabled && {
944
- manualChunks: createManualChunks()
945
- }
946
- }
947
- },
948
- // Increase warning limit since we're doing smart chunking
949
- chunkSizeWarningLimit: 500,
950
- // Inline small assets (< 4KB)
951
- assetsInlineLimit: 4096,
952
- // Enable terser minification for smaller bundles
953
- minify: "terser",
954
- terserOptions: {
955
- compress: {
956
- // Aggressive optimizations for production
957
- drop_console: false,
958
- // Keep console for debugging
959
- drop_debugger: true,
960
- pure_funcs: ["console.debug"],
961
- passes: 2
962
- },
963
- mangle: {
964
- properties: false
965
- },
966
- format: {
967
- comments: false
968
- }
969
- },
970
- // Disable source maps for smaller builds
971
- sourcemap: false,
972
- // CSS code splitting
973
- cssCodeSplit: true,
974
- // Target modern browsers (smaller bundles)
975
- target: "es2020"
976
- };
977
- return {
978
- build: buildConfig,
979
- // Optimize deps for faster dev startup
980
- optimizeDeps: {
981
- include: ["react", "react-dom"],
982
- exclude: ["olova"]
983
- },
984
- // SSR options
985
- ssr: {
986
- noExternal: ["olova"]
987
- },
988
- // esbuild optimizations
989
- esbuild: {
990
- treeShaking: true,
991
- legalComments: "none"
992
- },
993
- // Preview server configuration (for testing production builds)
994
- preview: {
995
- headers: {
996
- // Long-term caching for static assets
997
- "Cache-Control": "public, max-age=31536000"
998
- }
999
- }
1000
- };
1001
- },
1002
- async configResolved(resolvedConfig) {
1003
- config = resolvedConfig;
1004
- },
1005
- async resolveId(id) {
1006
- if (id === virtualModuleId || id === serverVirtualModuleId || id === workerVirtualModuleId) {
1007
- const isServer = id === serverVirtualModuleId;
1008
- const isWorker = id === workerVirtualModuleId;
1009
- const fileName = isWorker ? "entry-worker" : isServer ? "entry-server" : "main";
1010
- const possibleLocalPaths = [
1011
- path4.resolve(config.root, `src/${fileName}.tsx`),
1012
- path4.resolve(config.root, `src/${fileName}.ts`),
1013
- path4.resolve(config.root, `${fileName}.tsx`),
1014
- path4.resolve(config.root, `plugins/${fileName}.tsx`)
1015
- ];
1016
- for (const p of possibleLocalPaths) {
1017
- if (existsSync(p)) return p;
1018
- }
1019
- const exportName = isWorker ? "olova/entry-worker" : isServer ? "olova/entry-server" : "olova/main";
1020
- try {
1021
- const resolved = await this.resolve(exportName, void 0, { skipSelf: true });
1022
- if (resolved && !resolved.external) {
1023
- return resolved.id;
1024
- }
1025
- } catch (e) {
1026
- }
1027
- const pkgDir = path4.dirname(fileURLToPath(import.meta.url));
1028
- const possiblePkgPaths = [
1029
- path4.resolve(pkgDir, `dist/${fileName}.js`),
1030
- path4.resolve(pkgDir, `${fileName}.js`),
1031
- path4.resolve(pkgDir, `${fileName}.mjs`),
1032
- path4.resolve(pkgDir, `${fileName}.ts`),
1033
- path4.resolve(pkgDir, `${fileName}.tsx`),
1034
- path4.resolve(pkgDir, `src/${fileName}.tsx`),
1035
- path4.resolve(pkgDir, `src/${fileName}.ts`),
1036
- // If running from dist, check parent directories
1037
- path4.resolve(pkgDir, "..", `dist/${fileName}.js`),
1038
- path4.resolve(pkgDir, "..", `${fileName}.js`),
1039
- path4.resolve(pkgDir, "..", `${fileName}.tsx`),
1040
- path4.resolve(pkgDir, "..", `src/${fileName}.tsx`)
1041
- ];
1042
- for (const p of possiblePkgPaths) {
1043
- if (existsSync(p)) return p;
1044
- }
1045
- }
1046
- if (id === appVirtualModuleId) {
1047
- return resolvedAppVirtualModuleId;
1048
- }
1049
- return null;
1050
- },
1051
- load(id) {
1052
- if (id === resolvedAppVirtualModuleId) {
1053
- return `
1054
- export { OlovaRouter, Outlet, routes, layouts, notFoundPages } from '@/route.tree';
1055
- import '@/index.css';
1056
- `;
1057
- }
1058
- return null;
1059
- },
1060
- configureServer(server) {
1061
- const devBuildId = generateBuildId();
1062
- logger_default.printBanner();
1063
- server.middlewares.use(async (req, res, next) => {
1064
- const url = req.url?.split("?")[0];
1065
- if (url === "/" || url === "/index.html" || req.headers.accept?.includes("text/html") && !url?.match(/\.[a-z]+$/)) {
1066
- try {
1067
- logger_default.printSSRRender(url || "/");
1068
- let template = htmlContent;
1069
- const { render } = await server.ssrLoadModule(
1070
- serverVirtualModuleId
1071
- );
1072
- const renderResult = await render(url || "/");
1073
- const appHtml = typeof renderResult === "string" ? renderResult : renderResult.html;
1074
- const routeMetadata = typeof renderResult === "string" ? {} : renderResult.metadata || {};
1075
- const loaderData = typeof renderResult === "string" ? void 0 : renderResult.loaderData;
1076
- const queryState = typeof renderResult === "string" ? void 0 : renderResult.queryState;
1077
- let fullHtml = template.replace("<!--app-html-->", appHtml);
1078
- const hydrationData = {
1079
- route: url || "/",
1080
- params: {},
1081
- metadata: routeMetadata,
1082
- chunks: [],
1083
- // Dev mode doesn't have pre-built chunks
1084
- isStatic: false,
1085
- loaderData,
1086
- queryState
1087
- };
1088
- const flightScripts = generateOlovaHydration(hydrationData, devBuildId);
1089
- const jsonLdScript = generateJsonLd(hydrationData);
1090
- const resourceHints = `<link rel="dns-prefetch" href="//fonts.googleapis.com"><link rel="preconnect" href="https://fonts.googleapis.com" crossorigin>`;
1091
- if (fullHtml.includes("</head>")) {
1092
- fullHtml = fullHtml.replace("</head>", `${jsonLdScript}${resourceHints}</head>`);
1093
- }
1094
- if (fullHtml.includes("</body>")) {
1095
- fullHtml = fullHtml.replace(
1096
- "</body>",
1097
- `${flightScripts}<script type="module" src="/@id/${virtualModuleId}"></script></body>`
1098
- );
1099
- } else {
1100
- fullHtml += `${flightScripts}<script type="module" src="/@id/${virtualModuleId}"></script>`;
1101
- }
1102
- fullHtml = await server.transformIndexHtml(url || "/", fullHtml);
1103
- if (!fullHtml.trim().toLowerCase().startsWith("<!doctype html>")) {
1104
- fullHtml = `<!DOCTYPE html>${fullHtml}`;
1105
- }
1106
- res.statusCode = 200;
1107
- res.setHeader("Content-Type", "text/html");
1108
- res.end(minifyHtml(fullHtml));
1109
- return;
1110
- } catch (e) {
1111
- server.ssrFixStacktrace(e);
1112
- console.error(e);
1113
- res.statusCode = 500;
1114
- res.end(e.stack);
1115
- return;
1116
- }
1117
- }
1118
- next();
1119
- });
1120
- },
1121
- async writeBundle(_options, bundle) {
1122
- if (config.command === "serve" || config.build.ssr) return;
1123
- const buildStartTime = Date.now();
1124
- logger_default.printBuildStart();
1125
- const outDir = config.build.outDir;
1126
- const serverOutDir = path4.resolve(config.root, "dist/server");
1127
- const clientEntry = Object.values(bundle).find(
1128
- (chunk) => chunk.type === "chunk" && chunk.isEntry && (chunk.facadeModuleId?.includes("main") || chunk.name === "main")
1129
- );
1130
- const clientEntryFileName = clientEntry ? clientEntry.fileName : "assets/index.js";
1131
- const cssAssets = Object.values(bundle).filter(
1132
- (chunk) => chunk.type === "asset" && chunk.fileName.endsWith(".css")
1133
- );
1134
- const cssLinks = cssAssets.map(
1135
- (css) => `<link rel="stylesheet" crossorigin href="/${css.fileName}">`
1136
- ).join("");
1137
- await build({
1138
- configFile: config.configFile,
1139
- root: config.root,
1140
- build: {
1141
- ssr: true,
1142
- emptyOutDir: false,
1143
- outDir: serverOutDir,
1144
- minify: false,
1145
- // Keep readable for debugging
1146
- rollupOptions: {
1147
- input: { index: serverVirtualModuleId },
1148
- output: {
1149
- entryFileNames: "index.js",
1150
- chunkFileNames: "[name].js",
1151
- assetFileNames: "[name].[ext]",
1152
- format: "esm",
1153
- // Explicitly set to undefined to prevent client-side chunking config
1154
- manualChunks: void 0
1155
- }
1156
- }
1157
- },
1158
- logLevel: "error"
1159
- });
1160
- const serverEntryPath = path4.resolve(serverOutDir, "index.js");
1161
- const serverEntryUrl = pathToFileURL(serverEntryPath).toString();
1162
- const { routes } = await import(serverEntryUrl);
1163
- const paths = ["/"];
1164
- function extractPaths(routesArr) {
1165
- routesArr.forEach((r) => {
1166
- if (r.path && !r.path.includes("*") && !r.path.includes(":")) {
1167
- paths.push(r.path);
1168
- }
1169
- });
1170
- }
1171
- if (Array.isArray(routes)) extractPaths(routes);
1172
- if (Array.isArray(routes)) {
1173
- for (const r of routes) {
1174
- const isDynamic = r.path.includes(":") || r.path.includes("*");
1175
- if (r.getStaticPaths && isDynamic) {
1176
- try {
1177
- const staticPaths = await r.getStaticPaths();
1178
- if (Array.isArray(staticPaths)) {
1179
- for (const entry of staticPaths) {
1180
- let expandedPath = r.path;
1181
- if (entry.params) {
1182
- for (const [key, value] of Object.entries(entry.params)) {
1183
- expandedPath = expandedPath.replace(`:${key}`, value);
1184
- expandedPath = expandedPath.replace("*", value);
1185
- }
1186
- }
1187
- paths.push(expandedPath);
1188
- }
1189
- logger_default.info(`getStaticPaths for ${r.path}: ${staticPaths.length} paths expanded`);
1190
- }
1191
- } catch (e) {
1192
- logger_default.warn(`getStaticPaths() failed for ${r.path}: ${e.message}`);
1193
- }
1194
- }
1195
- }
1196
- }
1197
- if (options.staticPaths) {
1198
- options.staticPaths.forEach((p) => paths.push(p));
1199
- }
1200
- const uniquePaths = [...new Set(paths)];
1201
- const buildId = generateBuildId();
1202
- logger_default.printSSGStart(buildId);
1203
- const routeInfo = uniquePaths.map((p) => ({
1204
- path: p,
1205
- type: p.includes(":") || p.includes("*") ? "dynamic" : "static"
1206
- }));
1207
- logger_default.printRoutes(routeInfo);
1208
- logger_default.printFlightInfo();
1209
- const jsChunks = Object.values(bundle).filter((chunk) => chunk.type === "chunk" && chunk.fileName.endsWith(".js")).map((chunk) => chunk.fileName);
1210
- let successCount = 0;
1211
- let failCount = 0;
1212
- for (const p of uniquePaths) {
1213
- try {
1214
- if (typeof globalThis.window !== "undefined") {
1215
- globalThis.window.location.pathname = p;
1216
- }
1217
- const cacheBuster = `?t=${Date.now()}-${Math.random()}`;
1218
- const { render } = await import(serverEntryUrl + cacheBuster);
1219
- const renderResult = await render(p);
1220
- const appHtml = typeof renderResult === "string" ? renderResult : renderResult.html;
1221
- const routeMetadata = typeof renderResult === "string" ? {} : renderResult.metadata || {};
1222
- const loaderData = typeof renderResult === "string" ? void 0 : renderResult.loaderData;
1223
- const queryState = typeof renderResult === "string" ? void 0 : renderResult.queryState;
1224
- let html = htmlContent.replace("<!--app-html-->", appHtml);
1225
- const preloadHints = generatePreloadHints(jsChunks, clientEntryFileName);
1226
- const preloadTags = generatePreloadTags(preloadHints);
1227
- const hydrationData = {
1228
- route: p,
1229
- params: {},
1230
- metadata: routeMetadata,
1231
- chunks: jsChunks,
1232
- isStatic: true,
1233
- loaderData,
1234
- queryState
1235
- };
1236
- const flightScripts = generateOlovaHydration(hydrationData, buildId);
1237
- const jsonLdScript = generateJsonLd(hydrationData);
1238
- const resourceHints = generateResourceHints(hydrationData);
1239
- if (html.includes("</head>")) {
1240
- html = html.replace("</head>", `${jsonLdScript}${resourceHints}${cssLinks}${preloadTags}</head>`);
1241
- }
1242
- if (html.includes("</body>")) {
1243
- html = html.replace(
1244
- "</body>",
1245
- `${flightScripts}<script type="module" src="/${clientEntryFileName}"></script></body>`
1246
- );
1247
- } else {
1248
- html += `${flightScripts}<script type="module" src="/${clientEntryFileName}"></script>`;
1249
- }
1250
- if (!html.trim().toLowerCase().startsWith("<!doctype html>")) {
1251
- html = `<!DOCTYPE html>${html}`;
1252
- }
1253
- const itemPath = p === "/" ? "index.html" : `${p.substring(1)}/index.html`;
1254
- const finalPath = path4.resolve(outDir, itemPath);
1255
- await fs4.mkdir(path4.dirname(finalPath), { recursive: true });
1256
- await fs4.writeFile(finalPath, minifyHtml(html));
1257
- logger_default.printPageGenerated(itemPath, true);
1258
- successCount++;
1259
- } catch (e) {
1260
- logger_default.printPageError(p, e.message);
1261
- failCount++;
1262
- }
1263
- }
1264
- try {
1265
- const notFoundPath = "/__olova_404__";
1266
- if (typeof globalThis.window !== "undefined") {
1267
- globalThis.window.location.pathname = notFoundPath;
1268
- }
1269
- const cacheBuster404 = `?t=${Date.now()}-${Math.random()}`;
1270
- const { render: render404 } = await import(serverEntryUrl + cacheBuster404);
1271
- const result404 = await render404(notFoundPath);
1272
- const html404Content = typeof result404 === "string" ? result404 : result404.html;
1273
- let html404 = htmlContent.replace("<!--app-html-->", html404Content);
1274
- const preloadHints404 = generatePreloadHints(jsChunks, clientEntryFileName);
1275
- const preloadTags404 = generatePreloadTags(preloadHints404);
1276
- const hydrationData404 = {
1277
- route: "/404",
1278
- params: {},
1279
- metadata: { title: "404 - Page Not Found" },
1280
- chunks: jsChunks,
1281
- isStatic: true
1282
- };
1283
- const flightScripts404 = generateOlovaHydration(hydrationData404, buildId);
1284
- const jsonLd404 = generateJsonLd(hydrationData404);
1285
- const resourceHints404 = generateResourceHints(hydrationData404);
1286
- if (html404.includes("</head>")) {
1287
- html404 = html404.replace("</head>", `${jsonLd404}${resourceHints404}${cssLinks}${preloadTags404}</head>`);
1288
- }
1289
- if (html404.includes("</body>")) {
1290
- html404 = html404.replace("</body>", `${flightScripts404}<script type="module" src="/${clientEntryFileName}"></script></body>`);
1291
- } else {
1292
- html404 += `${flightScripts404}<script type="module" src="/${clientEntryFileName}"></script>`;
1293
- }
1294
- if (!html404.trim().toLowerCase().startsWith("<!doctype html>")) {
1295
- html404 = `<!DOCTYPE html>${html404}`;
1296
- }
1297
- const notFoundFinalPath = path4.resolve(outDir, "404.html");
1298
- await fs4.writeFile(notFoundFinalPath, minifyHtml(html404));
1299
- logger_default.printPageGenerated("404.html", true);
1300
- successCount++;
1301
- } catch (e) {
1302
- logger_default.warn(`Failed to generate 404.html: ${e.message}`);
1303
- }
1304
- await fs4.rm(serverOutDir, { recursive: true, force: true });
1305
- const buildTime = Date.now() - buildStartTime;
1306
- logger_default.printSSGComplete({
1307
- totalPages: successCount,
1308
- successPages: successCount,
1309
- failedPages: failCount,
1310
- buildTime
1311
- });
1312
- }
1313
- },
1314
- // Add performance optimization plugins
1315
- ...options.performance?.enabled !== false ? olovaPerformance(options.performance) : []
1316
- ];
1317
- }
1318
-
1319
- export { olova };
1320
- //# sourceMappingURL=olova.js.map
1321
- //# sourceMappingURL=olova.js.map