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
@@ -1,114 +0,0 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
- import type { ErrorEntry, LayoutEntry, LoadingEntry, MiddlewareEntry, NotFoundEntry, RouteEntry, ScanResult } from '../types';
4
- import { isRouteGroup, pathToRoute } from '../utils';
5
-
6
- const RESERVED_NAMES = new Set([
7
- 'page', 'layout', 'loading', 'error', 'not-found', 'middleware',
8
- 'route.tree',
9
- ]);
10
-
11
- function scanDirectory(
12
- dir: string,
13
- rootDir: string,
14
- extensions: string[],
15
- routes: RouteEntry[],
16
- notFoundPages: NotFoundEntry[],
17
- layouts: LayoutEntry[],
18
- loadingPages: LoadingEntry[],
19
- errorPages: ErrorEntry[],
20
- middlewares: MiddlewareEntry[],
21
- isRoot = false
22
- ) {
23
- if (!fs.existsSync(dir)) return;
24
-
25
- const entries = fs.readdirSync(dir, { withFileTypes: true });
26
-
27
- for (const entry of entries) {
28
- const fullPath = path.join(dir, entry.name);
29
-
30
- if (entry.isDirectory()) {
31
- if (entry.name === 'node_modules' || entry.name === 'assets' || entry.name.startsWith('_')) continue;
32
- scanDirectory(fullPath, rootDir, extensions, routes, notFoundPages, layouts, loadingPages, errorPages, middlewares, false);
33
- } else if (entry.isFile()) {
34
- const ext = path.extname(entry.name);
35
- const baseName = path.basename(entry.name, ext);
36
-
37
- if (baseName === 'layout' && extensions.includes(ext)) {
38
- const relativePath = path.relative(rootDir, dir);
39
- const { routePath } = pathToRoute(relativePath, path.sep);
40
- layouts.push({
41
- path: isRoot ? '/' : routePath,
42
- filePath: fullPath
43
- });
44
- }
45
- else if (baseName === 'loading' && extensions.includes(ext)) {
46
- const relativePath = path.relative(rootDir, dir);
47
- const { routePath } = pathToRoute(relativePath, path.sep);
48
- loadingPages.push({
49
- path: isRoot ? '/' : routePath,
50
- filePath: fullPath
51
- });
52
- }
53
- else if (baseName === 'error' && extensions.includes(ext)) {
54
- const relativePath = path.relative(rootDir, dir);
55
- const { routePath } = pathToRoute(relativePath, path.sep);
56
- errorPages.push({
57
- path: isRoot ? '/' : routePath,
58
- filePath: fullPath
59
- });
60
- }
61
- else if (baseName === 'middleware' && extensions.includes(ext)) {
62
- const relativePath = path.relative(rootDir, dir);
63
- const { routePath } = pathToRoute(relativePath, path.sep);
64
- middlewares.push({
65
- path: isRoot ? '/' : routePath,
66
- filePath: fullPath
67
- });
68
- }
69
- else if ((baseName === 'not-found' || baseName === '404') && extensions.includes(ext)) {
70
- const relativeParts = path.relative(rootDir, dir).split(path.sep).filter(Boolean);
71
- const filteredParts = relativeParts.filter(p => !isRouteGroup(p));
72
- const pathPrefix = isRoot ? '' : '/' + filteredParts.join('/');
73
- notFoundPages.push({ pathPrefix: pathPrefix || '', filePath: fullPath });
74
- } else if (baseName === 'page' && extensions.includes(ext)) {
75
- const relativePath = path.relative(rootDir, dir);
76
- const { routePath, params } = pathToRoute(relativePath, path.sep);
77
- routes.push({ path: routePath, filePath: fullPath, isDynamic: params.length > 0, params });
78
- } else if (
79
- !RESERVED_NAMES.has(baseName) &&
80
- !baseName.endsWith('.d') &&
81
- !baseName.startsWith('_') &&
82
- extensions.includes(ext)
83
- ) {
84
- const relativePath = path.relative(rootDir, fullPath);
85
- const relativePathNoExt = relativePath.substring(0, relativePath.length - ext.length);
86
- const { routePath, params } = pathToRoute(relativePathNoExt, path.sep);
87
- routes.push({ path: routePath, filePath: fullPath, isDynamic: params.length > 0, params });
88
- }
89
- }
90
- }
91
- }
92
-
93
- export function scanRoutes(rootDir: string, extensions: string[]): ScanResult {
94
- const routes: RouteEntry[] = [];
95
- const notFoundPages: NotFoundEntry[] = [];
96
- const layouts: LayoutEntry[] = [];
97
- const loadingPages: LoadingEntry[] = [];
98
- const errorPages: ErrorEntry[] = [];
99
- const middlewares: MiddlewareEntry[] = [];
100
- const absoluteRoot = path.isAbsolute(rootDir) ? rootDir : path.resolve(rootDir);
101
-
102
- if (!fs.existsSync(absoluteRoot)) {
103
- throw new Error(`Olova Router: Root directory does not exist: ${absoluteRoot}`);
104
- }
105
-
106
- scanDirectory(absoluteRoot, absoluteRoot, extensions, routes, notFoundPages, layouts, loadingPages, errorPages, middlewares, true);
107
- routes.sort((a, b) => (a.isDynamic !== b.isDynamic ? (a.isDynamic ? 1 : -1) : a.path.localeCompare(b.path)));
108
- notFoundPages.sort((a, b) => b.pathPrefix.length - a.pathPrefix.length);
109
- layouts.sort((a, b) => a.path.length - b.path.length);
110
- loadingPages.sort((a, b) => b.path.length - a.path.length);
111
- errorPages.sort((a, b) => b.path.length - a.path.length);
112
- middlewares.sort((a, b) => a.path.length - b.path.length);
113
- return { routes, notFoundPages, layouts, loadingPages, errorPages, middlewares };
114
- }
@@ -1,190 +0,0 @@
1
- import type { ComponentType, ReactNode } from 'react';
2
- export type { PluginOption, ResolvedConfig } from 'vite';
3
-
4
- export interface RouteEntry {
5
- path: string;
6
- filePath: string;
7
- isDynamic: boolean;
8
- params: string[];
9
- }
10
-
11
- export interface ScannerOptions {
12
- rootDir: string;
13
- extensions: string[];
14
- }
15
-
16
- export interface Metadata {
17
- title?: string;
18
- description?: string;
19
- keywords?: string[];
20
- openGraph?: {
21
- title?: string;
22
- description?: string;
23
- image?: string;
24
- url?: string;
25
- type?: string;
26
- };
27
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
28
- [key: string]: any;
29
- }
30
-
31
- export interface RouteConfig {
32
- path: string;
33
- component: string;
34
- params?: string[];
35
- metadata?: Metadata;
36
- }
37
-
38
- export interface GeneratorOptions {
39
- routes: RouteEntry[];
40
- basePath: string;
41
- }
42
-
43
- export interface OlovaRouterOptions {
44
- extensions?: string[];
45
- packageName?: string;
46
- }
47
-
48
- export interface DynamicSegmentResult {
49
- isDynamic: boolean;
50
- paramName: string | null;
51
- }
52
-
53
- export interface NotFoundEntry {
54
- pathPrefix: string;
55
- filePath: string;
56
- }
57
-
58
- export interface NotFoundPageConfig {
59
- pathPrefix: string;
60
- component: ComponentType;
61
- metadata?: Metadata;
62
- }
63
-
64
- export interface LayoutEntry {
65
- path: string;
66
- filePath: string;
67
- }
68
-
69
- export interface LoadingEntry {
70
- path: string;
71
- filePath: string;
72
- }
73
-
74
- export interface ErrorEntry {
75
- path: string;
76
- filePath: string;
77
- }
78
-
79
- export interface MiddlewareEntry {
80
- path: string;
81
- filePath: string;
82
- }
83
-
84
- export interface ScanResult {
85
- routes: RouteEntry[];
86
- notFoundPages: NotFoundEntry[];
87
- layouts: LayoutEntry[];
88
- loadingPages: LoadingEntry[];
89
- errorPages: ErrorEntry[];
90
- middlewares: MiddlewareEntry[];
91
- }
92
-
93
- export interface RouteWithExport extends RouteConfig {
94
- hasDefault: boolean;
95
- namedExport: string | null;
96
- hasMetadata: boolean;
97
- metadataSource?: string;
98
- hasRoute: boolean;
99
- hasGetStaticPaths: boolean;
100
- hasLoader: boolean;
101
- }
102
-
103
- export interface NotFoundWithExport extends NotFoundEntry {
104
- hasDefault: boolean;
105
- namedExport: string | null;
106
- hasMetadata: boolean;
107
- }
108
-
109
- export interface LayoutWithExport extends LayoutEntry {
110
- hasDefault: boolean;
111
- namedExport: string | null;
112
- hasMetadata: boolean;
113
- }
114
-
115
- export interface LoadingWithExport extends LoadingEntry {
116
- hasDefault: boolean;
117
- namedExport: string | null;
118
- }
119
-
120
- export interface ErrorWithExport extends ErrorEntry {
121
- hasDefault: boolean;
122
- namedExport: string | null;
123
- }
124
-
125
- export interface MiddlewareWithExport extends MiddlewareEntry {
126
- hasDefault: boolean;
127
- namedExport: string | null;
128
- }
129
-
130
- export interface RouteErrorProps {
131
- error: Error;
132
- reset: () => void;
133
- }
134
-
135
- export interface LoaderContext {
136
- request: Request;
137
- params: Record<string, string>;
138
- }
139
-
140
- export interface Route {
141
- id?: string;
142
- path: string;
143
- component?: ComponentType;
144
- metadata?: Metadata;
145
- loading?: ComponentType;
146
- error?: ComponentType<RouteErrorProps>;
147
- filePath?: string;
148
- params?: string[];
149
- loader?: (ctx: LoaderContext) => Promise<Record<string, unknown>>;
150
- }
151
-
152
- export interface LayoutRoute {
153
- path: string;
154
- layout?: ComponentType;
155
- children: Route[];
156
- metadata?: Metadata;
157
- }
158
-
159
- export type SearchParams = Record<string, string | string[]>;
160
-
161
- export interface SetSearchParamsOptions {
162
- replace?: boolean;
163
- merge?: boolean;
164
- }
165
-
166
- export interface NavigateOptions {
167
- scroll?: boolean;
168
- replace?: boolean;
169
- }
170
-
171
- export type NavigationListener = (from: string, to: string) => void | boolean;
172
-
173
- export interface RouterContextType {
174
- currentPath: string;
175
- params: Record<string, string>;
176
- searchParams: SearchParams;
177
- navigate: (path: string, options?: NavigateOptions) => void;
178
- push: (path: string, options?: NavigateOptions) => void;
179
- replace: (path: string, options?: NavigateOptions) => void;
180
- back: () => void;
181
- forward: () => void;
182
- refresh: () => void;
183
- setSearchParams: (params: Record<string, string | string[] | null>, options?: SetSearchParamsOptions) => void;
184
- isNavigating: boolean;
185
- prefetch: (path: string) => void;
186
- }
187
-
188
- export interface OutletContextType {
189
- content: ReactNode;
190
- }
@@ -1,85 +0,0 @@
1
- import fs from 'fs';
2
-
3
- function extractBalancedBraces(content: string, startIndex: number): string | null {
4
- if (content[startIndex] !== '{') return null;
5
- let depth = 0;
6
- for (let i = startIndex; i < content.length; i++) {
7
- if (content[i] === '{') depth++;
8
- else if (content[i] === '}') {
9
- depth--;
10
- if (depth === 0) return content.substring(startIndex, i + 1);
11
- }
12
- }
13
- return null;
14
- }
15
-
16
- function extractMetadataSource(content: string): string | undefined {
17
- const match = content.match(/export\s+(?:const|let|var)\s+metadata\s*(?::\s*\w+\s*)?=\s*/);
18
- if (!match || match.index === undefined) return undefined;
19
- const afterEquals = match.index + match[0].length;
20
- return extractBalancedBraces(content, afterEquals) || undefined;
21
- }
22
-
23
- export function detectExportType(filePath: string): {
24
- hasDefault: boolean;
25
- namedExport: string | null;
26
- hasMetadata: boolean;
27
- metadataSource?: string;
28
- hasRoute: boolean;
29
- hasGetStaticPaths: boolean;
30
- hasLoader: boolean;
31
- } {
32
- try {
33
- const content = fs.readFileSync(filePath, 'utf-8');
34
-
35
- const hasMetadata = /export\s+(?:const|let|var)\s+metadata\s*/.test(content) ||
36
- /export\s*\{\s*metadata\s*\}/.test(content);
37
-
38
- const hasRoute = /export\s+(?:const|let|var)\s+route\s*=/.test(content) ||
39
- /export\s*\{\s*route\s*\}/.test(content);
40
-
41
- const hasGetStaticPaths = /export\s+(?:async\s+)?function\s+getStaticPaths/.test(content) ||
42
- /export\s+(?:const|let|var)\s+getStaticPaths\s*=/.test(content) ||
43
- /export\s*\{[^}]*getStaticPaths[^}]*\}/.test(content);
44
-
45
- const hasLoader = /export\s+(?:async\s+)?function\s+loader/.test(content) ||
46
- /export\s+(?:const|let|var)\s+loader\s*=/.test(content) ||
47
- /export\s*\{[^}]*loader[^}]*\}/.test(content);
48
-
49
- // Extract metadata source for inlining (works for any file type with object literal metadata)
50
- const metadataSource = extractMetadataSource(content);
51
-
52
- if (filePath.toLowerCase().endsWith('.mdx')) {
53
- return {
54
- hasDefault: true,
55
- namedExport: null,
56
- hasMetadata: !!metadataSource,
57
- hasRoute: false,
58
- hasGetStaticPaths: false,
59
- hasLoader: false,
60
- metadataSource
61
- };
62
- }
63
-
64
- if (/export\s+default\s+/.test(content)) {
65
- return { hasDefault: true, namedExport: null, hasMetadata, metadataSource, hasRoute, hasGetStaticPaths, hasLoader };
66
- }
67
-
68
- const namedMatch = content.match(/export\s+(?:const|function|class)\s+(\w+)/);
69
- if (namedMatch) {
70
- return { hasDefault: false, namedExport: namedMatch[1], hasMetadata, metadataSource, hasRoute, hasGetStaticPaths, hasLoader };
71
- }
72
-
73
- const exportMatch = content.match(/export\s*\{\s*(\w+)(?:\s+as\s+default)?\s*\}/);
74
- if (exportMatch) {
75
- if (content.includes('as default')) {
76
- return { hasDefault: true, namedExport: null, hasMetadata, metadataSource, hasRoute, hasGetStaticPaths, hasLoader };
77
- }
78
- return { hasDefault: false, namedExport: exportMatch[1], hasMetadata, metadataSource, hasRoute, hasGetStaticPaths, hasLoader };
79
- }
80
-
81
- return { hasDefault: false, namedExport: null, hasMetadata, metadataSource, hasRoute, hasGetStaticPaths, hasLoader };
82
- } catch {
83
- return { hasDefault: true, namedExport: null, hasMetadata: false, hasRoute: false, hasGetStaticPaths: false, hasLoader: false };
84
- }
85
- }
@@ -1,4 +0,0 @@
1
- export { detectExportType } from './export';
2
- export { getRouteName } from './naming';
3
- export { isRouteGroup, parseDynamicSegment, pathToRoute } from './path';
4
-
@@ -1,54 +0,0 @@
1
- export function getRouteName(path: string): string {
2
- if (path === '/' || path === '') return 'Root';
3
-
4
- // Remove leading/trailing slashes and split
5
- const segments = path.replace(/^\/|\/$/g, '').split(/[\\/]/);
6
-
7
- const nameSegments = segments.map(segment => {
8
- let cleanSegment = segment;
9
-
10
- // Handle dynamic segments :id -> Id
11
- if (cleanSegment.startsWith(':')) {
12
- cleanSegment = cleanSegment.slice(1);
13
- }
14
-
15
- // Handle bracket syntax [id] -> Id, [...slug] -> Slug
16
- if (cleanSegment.startsWith('[') && cleanSegment.endsWith(']')) {
17
- cleanSegment = cleanSegment.slice(1, -1);
18
- if (cleanSegment.startsWith('...')) {
19
- cleanSegment = cleanSegment.slice(3);
20
- }
21
- }
22
-
23
- // Handle catch-all * -> All
24
- if (cleanSegment === '*') {
25
- return 'All';
26
- }
27
-
28
- // Skip 'index' segment for cleaner names
29
- if (cleanSegment.toLowerCase() === 'index') {
30
- return '';
31
- }
32
-
33
- // Handle route groups (auth) -> Auth or ignore? Usually people ignore path but name might want it?
34
- // User request is about "main folder name". (auth) is usually invisible.
35
- // Let's strip parens but keep name for uniqueness? Or just strip?
36
- // scanner often treats (group) as transparent for path, but for Naming it might be good to include or exclude.
37
- // Let's include it cleaned up to ensure Uniqueness (e.g. /app/(dashboard)/layout vs /app/(marketing)/layout).
38
- if (cleanSegment.startsWith('(') && cleanSegment.endsWith(')')) {
39
- cleanSegment = cleanSegment.slice(1, -1);
40
- }
41
-
42
- return capitalize(cleanSegment);
43
- });
44
-
45
- const finalName = nameSegments.join('');
46
- if (/^\d/.test(finalName)) {
47
- return 'Page' + finalName;
48
- }
49
- return finalName;
50
- }
51
-
52
- function capitalize(str: string): string {
53
- return str.charAt(0).toUpperCase() + str.slice(1).replace(/[^a-zA-Z0-9]/g, '');
54
- }
package/src/utils/path.ts DELETED
@@ -1,45 +0,0 @@
1
- import type { DynamicSegmentResult } from '../types';
2
-
3
- export function parseDynamicSegment(segment: string): DynamicSegmentResult & { isCatchAll: boolean } {
4
- if (segment.match(/^\[\.\.\.(.+)\]$/)) {
5
- const paramName = segment.match(/^\[\.\.\.(.+)\]$/)?.[1] || 'slug';
6
- return { isDynamic: true, paramName, isCatchAll: true };
7
- }
8
-
9
- const bracketMatch = segment.match(/^\[(.+)\]$/);
10
- if (bracketMatch) {
11
- return { isDynamic: true, paramName: bracketMatch[1], isCatchAll: false };
12
- }
13
-
14
-
15
-
16
- return { isDynamic: false, paramName: null, isCatchAll: false };
17
- }
18
-
19
- export function isRouteGroup(segment: string): boolean {
20
- return /^\(.+\)$/.test(segment);
21
- }
22
-
23
- export function pathToRoute(relativePath: string, sep: string) {
24
- const params: string[] = [];
25
- let hasCatchAll = false;
26
- const segments = relativePath.split(sep).filter(Boolean);
27
-
28
- const routeSegments = segments
29
- .filter(segment => !isRouteGroup(segment))
30
- .map(segment => {
31
- const { isDynamic, paramName, isCatchAll } = parseDynamicSegment(segment);
32
- if (isDynamic && paramName) {
33
- params.push(paramName);
34
- if (isCatchAll) {
35
- hasCatchAll = true;
36
- return `*`;
37
- }
38
- return `:${paramName}`;
39
- }
40
- return segment;
41
- });
42
-
43
- const routePath = '/' + routeSegments.join('/');
44
- return { routePath: routePath === '/' ? '/' : routePath, params, hasCatchAll };
45
- }
package/tsup.config.ts DELETED
@@ -1,35 +0,0 @@
1
- import { defineConfig } from 'tsup';
2
-
3
- export default defineConfig({
4
- entry: {
5
- 'olova': 'olova.ts',
6
- 'main': 'src/main.tsx',
7
- 'entry-server': 'src/entry-server.tsx',
8
- 'entry-worker': 'src/entry-worker.tsx',
9
- 'performance': 'src/performance.ts',
10
- 'router': 'src/router/router.tsx',
11
- },
12
- format: ['cjs', 'esm'],
13
- dts: {
14
- entry: {
15
- 'olova': 'olova.ts',
16
- 'router': 'src/router/router.tsx',
17
- }
18
- },
19
- clean: true,
20
- shims: true,
21
- platform: 'node',
22
- external: [
23
- 'vite',
24
- 'react',
25
- 'react-dom',
26
- 'react-dom/client',
27
- 'react-dom/server',
28
- 'virtual:olova-app', // External - resolved at runtime by plugin
29
- '/src/index.css' // External - resolved by Vite in user project
30
- ],
31
- treeshake: true,
32
- sourcemap: true,
33
- minify: false,
34
- target: 'node18',
35
- });