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,204 +0,0 @@
1
- import mdx from '@mdx-js/rollup';
2
- import fs from 'fs';
3
- import path from 'path';
4
- import type { Plugin } from 'vite';
5
- import { generateRouteTree } from '../generator';
6
- import { scanRoutes } from '../scanner';
7
- import type { ErrorWithExport, LayoutWithExport, LoadingWithExport, MiddlewareWithExport, NotFoundWithExport, OlovaRouterOptions, PluginOption, ResolvedConfig, RouteWithExport } from '../types';
8
- import { detectExportType, getRouteName } from '../utils';
9
-
10
- export function olovaRouter(options: OlovaRouterOptions = {}): PluginOption[] {
11
- const rootDir = options.rootDir || 'src/app';
12
- const extensions = options.extensions || ['.tsx', '.ts', '.mdx'];
13
- const packageName = options.packageName || 'olovastart';
14
-
15
- let config: ResolvedConfig;
16
- let absoluteRootDir: string;
17
- let watcher: fs.FSWatcher | null = null;
18
- let timer: NodeJS.Timeout | null = null;
19
-
20
- function generateRouteTreeFile() {
21
- const { routes, notFoundPages, layouts, loadingPages, errorPages, middlewares } = scanRoutes(absoluteRootDir, extensions);
22
-
23
- const routeConfigs: RouteWithExport[] = routes.map(r => {
24
- let exportInfo = detectExportType(r.filePath);
25
-
26
- if (r.filePath.toLowerCase().endsWith('.mdx')) {
27
- exportInfo = {
28
- ...exportInfo,
29
- hasDefault: true,
30
- namedExport: null
31
- };
32
- }
33
-
34
- return {
35
- path: r.path,
36
- component: r.filePath.replace(/\\/g, '/'),
37
- params: r.params.length > 0 ? r.params : undefined,
38
- hasDefault: exportInfo.hasDefault,
39
- namedExport: exportInfo.namedExport,
40
- hasMetadata: exportInfo.hasMetadata,
41
- metadataSource: exportInfo.metadataSource,
42
- hasRoute: exportInfo.hasRoute,
43
- hasGetStaticPaths: exportInfo.hasGetStaticPaths,
44
- hasLoader: exportInfo.hasLoader
45
- };
46
- });
47
-
48
- const notFoundConfigs: NotFoundWithExport[] = notFoundPages.map(nf => {
49
- const exportInfo = detectExportType(nf.filePath);
50
- return {
51
- pathPrefix: nf.pathPrefix,
52
- filePath: nf.filePath.replace(/\\/g, '/'),
53
- hasDefault: exportInfo.hasDefault,
54
- namedExport: exportInfo.namedExport,
55
- hasMetadata: exportInfo.hasMetadata
56
- };
57
- });
58
-
59
- const layoutConfigs: LayoutWithExport[] = layouts.map(l => {
60
- const exportInfo = detectExportType(l.filePath);
61
- return {
62
- path: l.path,
63
- filePath: l.filePath.replace(/\\/g, '/'),
64
- hasDefault: exportInfo.hasDefault,
65
- namedExport: exportInfo.namedExport,
66
- hasMetadata: exportInfo.hasMetadata
67
- };
68
- });
69
-
70
- const loadingConfigs: LoadingWithExport[] = loadingPages.map(lp => {
71
- const exportInfo = detectExportType(lp.filePath);
72
- return {
73
- path: lp.path,
74
- filePath: lp.filePath.replace(/\\/g, '/'),
75
- hasDefault: exportInfo.hasDefault,
76
- namedExport: exportInfo.namedExport,
77
- };
78
- });
79
-
80
- const errorConfigs: ErrorWithExport[] = errorPages.map(ep => {
81
- const exportInfo = detectExportType(ep.filePath);
82
- return {
83
- path: ep.path,
84
- filePath: ep.filePath.replace(/\\/g, '/'),
85
- hasDefault: exportInfo.hasDefault,
86
- namedExport: exportInfo.namedExport,
87
- };
88
- });
89
-
90
- const middlewareConfigs: MiddlewareWithExport[] = middlewares.map(mw => {
91
- const exportInfo = detectExportType(mw.filePath);
92
- return {
93
- path: mw.path,
94
- filePath: mw.filePath.replace(/\\/g, '/'),
95
- hasDefault: exportInfo.hasDefault,
96
- namedExport: exportInfo.namedExport,
97
- };
98
- });
99
-
100
- const content = generateRouteTree(routeConfigs, notFoundConfigs, layoutConfigs, loadingConfigs, errorConfigs, middlewareConfigs, absoluteRootDir, packageName);
101
- const treePath = path.resolve(config.root, 'src', 'route.tree.ts');
102
-
103
- const existing = fs.existsSync(treePath) ? fs.readFileSync(treePath, 'utf-8') : '';
104
- if (existing !== content) {
105
- fs.writeFileSync(treePath, content);
106
- console.log('\x1b[32m[olova]\x1b[0m Route tree updated');
107
- }
108
- }
109
-
110
- function startWatcher() {
111
- if (watcher) return;
112
-
113
- watcher = fs.watch(absoluteRootDir, { recursive: true }, (eventType, filename) => {
114
- if (!filename) return;
115
- if (filename.includes('route.tree.ts')) return;
116
-
117
- const ext = path.extname(filename);
118
- const isConfiguredExtension = extensions.includes(ext);
119
-
120
- const isPageFile = (filename.includes('page') && isConfiguredExtension);
121
- const is404File = (filename.includes('404') && isConfiguredExtension);
122
- const isNotFoundFile = (filename.includes('not-found') && isConfiguredExtension);
123
- const isLayoutFile = (filename.includes('layout') && isConfiguredExtension);
124
- const isLoadingFile = (filename.includes('loading') && isConfiguredExtension);
125
- const isErrorFile = (filename.includes('error') && isConfiguredExtension);
126
- const isMiddlewareFile = (filename.includes('middleware') && isConfiguredExtension);
127
- const isDirectory = !filename.includes('.');
128
- const isDynamicSegment = filename.includes('[');
129
- const isRenameEvent = eventType === 'rename';
130
-
131
- if (isPageFile || is404File || isNotFoundFile || isLayoutFile || isLoadingFile || isErrorFile || isMiddlewareFile || isDirectory || isDynamicSegment || isRenameEvent) {
132
- if (isPageFile && filename) {
133
- const fullPath = path.join(absoluteRootDir, filename);
134
- if (fs.existsSync(fullPath)) {
135
- const stat = fs.statSync(fullPath);
136
- if (stat.size === 0 && !filename.endsWith('.mdx')) {
137
- const relativeDir = path.relative(absoluteRootDir, path.dirname(fullPath));
138
- const pascalCaseName = getRouteName(relativeDir);
139
-
140
- const boilerplate = `
141
- export const metadata = {
142
- title: "${pascalCaseName}",
143
- description: "${pascalCaseName} page",
144
- }
145
-
146
- export default function ${pascalCaseName}() {
147
- return (
148
- <div>
149
- <h1>${pascalCaseName}</h1>
150
- </div>
151
- );
152
- }
153
- `;
154
- fs.writeFileSync(fullPath, boilerplate);
155
- console.log(`\x1b[32m[olova]\x1b[0m Generated boilerplate for ${filename}`);
156
- }
157
- }
158
- }
159
-
160
- if (timer) clearTimeout(timer);
161
- timer = setTimeout(() => {
162
- try {
163
- generateRouteTreeFile();
164
- } catch (error) {
165
- console.error('\x1b[31m[olova]\x1b[0m Error generating route tree:', error);
166
- }
167
- }, 100);
168
- }
169
- });
170
-
171
- console.log('\x1b[32m[olova]\x1b[0m Watching for route changes...');
172
- }
173
-
174
- const routerPlugin: Plugin = {
175
- name: 'olova-router',
176
-
177
- configResolved(resolvedConfig: ResolvedConfig) {
178
- config = resolvedConfig;
179
- absoluteRootDir = path.resolve(config.root, rootDir);
180
- },
181
-
182
- buildStart() {
183
- generateRouteTreeFile();
184
-
185
- if (config.command === 'serve') {
186
- startWatcher();
187
- }
188
- },
189
-
190
- buildEnd() {
191
- if (watcher) {
192
- watcher.close();
193
- watcher = null;
194
- }
195
- },
196
- };
197
-
198
- return [
199
- { enforce: 'pre', ...mdx() } as Plugin,
200
- routerPlugin
201
- ];
202
- }
203
-
204
- export default olovaRouter;
@@ -1,145 +0,0 @@
1
- import { Component, type ComponentType, type ErrorInfo, type ReactNode } from 'react';
2
-
3
- interface ErrorBoundaryProps {
4
- children: ReactNode;
5
- fallback?: ReactNode;
6
- }
7
-
8
- interface ErrorBoundaryState {
9
- hasError: boolean;
10
- error: Error | null;
11
- }
12
-
13
- export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
14
- constructor(props: ErrorBoundaryProps) {
15
- super(props);
16
- this.state = { hasError: false, error: null };
17
- }
18
-
19
- static getDerivedStateFromError(error: Error): ErrorBoundaryState {
20
- return { hasError: true, error };
21
- }
22
-
23
- componentDidCatch(error: Error, errorInfo: ErrorInfo) {
24
- console.error('[olova] Uncaught error:', error, errorInfo);
25
- }
26
-
27
- handleReset = () => {
28
- this.setState({ hasError: false, error: null });
29
- };
30
-
31
- render() {
32
- if (this.state.hasError) {
33
- if (this.props.fallback) {
34
- return this.props.fallback;
35
- }
36
-
37
- return (
38
- <div style={{
39
- padding: '2rem',
40
- maxWidth: '600px',
41
- margin: '4rem auto',
42
- fontFamily: 'system-ui, -apple-system, sans-serif',
43
- textAlign: 'center',
44
- }}>
45
- <h1 style={{ fontSize: '2rem', marginBottom: '1rem', color: '#e11d48' }}>
46
- Something went wrong
47
- </h1>
48
- <p style={{ color: '#6b7280', marginBottom: '1.5rem' }}>
49
- An unexpected error occurred while rendering this page.
50
- </p>
51
- {this.state.error && (
52
- <pre style={{
53
- padding: '1rem',
54
- backgroundColor: '#f3f4f6',
55
- borderRadius: '0.5rem',
56
- fontSize: '0.875rem',
57
- textAlign: 'left',
58
- overflow: 'auto',
59
- marginBottom: '1.5rem',
60
- color: '#374151',
61
- }}>
62
- {this.state.error.message}
63
- </pre>
64
- )}
65
- <div style={{ display: 'flex', gap: '0.75rem', justifyContent: 'center' }}>
66
- <button
67
- onClick={this.handleReset}
68
- style={{
69
- padding: '0.5rem 1.5rem',
70
- backgroundColor: '#3b82f6',
71
- color: 'white',
72
- border: 'none',
73
- borderRadius: '0.375rem',
74
- cursor: 'pointer',
75
- fontSize: '0.875rem',
76
- }}
77
- >
78
- Try Again
79
- </button>
80
- <button
81
- onClick={() => window.location.reload()}
82
- style={{
83
- padding: '0.5rem 1.5rem',
84
- backgroundColor: 'transparent',
85
- color: '#3b82f6',
86
- border: '1px solid #3b82f6',
87
- borderRadius: '0.375rem',
88
- cursor: 'pointer',
89
- fontSize: '0.875rem',
90
- }}
91
- >
92
- Reload Page
93
- </button>
94
- </div>
95
- </div>
96
- );
97
- }
98
-
99
- return this.props.children;
100
- }
101
- }
102
-
103
- interface RouteErrorBoundaryProps {
104
- children: ReactNode;
105
- fallbackComponent: ComponentType<{ error: Error; reset: () => void }>;
106
- routePath: string;
107
- }
108
-
109
- interface RouteErrorBoundaryState {
110
- hasError: boolean;
111
- error: Error | null;
112
- }
113
-
114
- export class RouteErrorBoundary extends Component<RouteErrorBoundaryProps, RouteErrorBoundaryState> {
115
- constructor(props: RouteErrorBoundaryProps) {
116
- super(props);
117
- this.state = { hasError: false, error: null };
118
- }
119
-
120
- static getDerivedStateFromError(error: Error): RouteErrorBoundaryState {
121
- return { hasError: true, error };
122
- }
123
-
124
- componentDidUpdate(prevProps: RouteErrorBoundaryProps) {
125
- if (prevProps.routePath !== this.props.routePath && this.state.hasError) {
126
- this.setState({ hasError: false, error: null });
127
- }
128
- }
129
-
130
- componentDidCatch(error: Error, errorInfo: ErrorInfo) {
131
- console.error('[olova] Route error:', error, errorInfo);
132
- }
133
-
134
- handleReset = () => {
135
- this.setState({ hasError: false, error: null });
136
- };
137
-
138
- render() {
139
- if (this.state.hasError && this.state.error) {
140
- const FallbackComponent = this.props.fallbackComponent;
141
- return <FallbackComponent error={this.state.error} reset={this.handleReset} />;
142
- }
143
- return this.props.children;
144
- }
145
- }
@@ -1,117 +0,0 @@
1
- import { useCallback, useEffect, useRef, type AnchorHTMLAttributes, type ReactNode } from 'react';
2
- import { useRouter } from './context';
3
-
4
- type ResolveSegment<S extends string> =
5
- S extends `:${string}` ? string :
6
- S extends '*' ? string :
7
- S;
8
-
9
- type ResolvePathSegments<Path extends string> =
10
- Path extends `${infer Segment}/${infer Rest}`
11
- ? `${ResolveSegment<Segment>}/${ResolvePathSegments<Rest>}`
12
- : ResolveSegment<Path>;
13
-
14
- export type ResolveRoutePath<Path extends string> =
15
- Path extends `${infer Base}/*`
16
- ? `${ResolvePathSegments<Base>}/${string}`
17
- : ResolvePathSegments<Path>;
18
-
19
- type ResolveRoutes<T extends string> = T extends string ? ResolveRoutePath<T> : never;
20
-
21
- interface LinkProps<T extends string> extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'href'> {
22
- href: ResolveRoutes<T>;
23
- children: ReactNode;
24
- className?: string;
25
- activeClassName?: string;
26
- exactActiveClassName?: string;
27
- prefetch?: boolean | 'hover' | 'viewport';
28
- replace?: boolean;
29
- scroll?: boolean;
30
- target?: string;
31
- }
32
-
33
- function isActive(currentPath: string, href: string, exact: boolean): boolean {
34
- const normalizedCurrent = currentPath.replace(/\/$/, '') || '/';
35
- const normalizedHref = (href.split('?')[0]).replace(/\/$/, '') || '/';
36
- if (exact) return normalizedCurrent === normalizedHref;
37
- if (normalizedHref === '/') return normalizedCurrent === '/';
38
- return normalizedCurrent === normalizedHref || normalizedCurrent.startsWith(normalizedHref + '/');
39
- }
40
-
41
- export function createLink<T extends string>() {
42
- const Link = ({
43
- href,
44
- children,
45
- className,
46
- activeClassName,
47
- exactActiveClassName,
48
- prefetch = 'hover',
49
- replace: shouldReplace = false,
50
- scroll = true,
51
- target,
52
- ...rest
53
- }: LinkProps<T>) => {
54
- const { push, replace, currentPath, prefetch: prefetchRoute } = useRouter();
55
- const isSSR = typeof window === 'undefined';
56
- const linkRef = useRef<HTMLAnchorElement>(null);
57
- const prefetched = useRef(false);
58
-
59
- const isExactActive = isActive(currentPath, href, true);
60
- const isPartialActive = isActive(currentPath, href, false);
61
-
62
- const computedClassName = [
63
- className,
64
- isPartialActive && activeClassName,
65
- isExactActive && exactActiveClassName,
66
- ].filter(Boolean).join(' ') || undefined;
67
-
68
- const handlePrefetch = useCallback(() => {
69
- if (!prefetched.current && prefetch !== false) {
70
- prefetched.current = true;
71
- prefetchRoute(href);
72
- }
73
- }, [href, prefetch, prefetchRoute]);
74
-
75
- useEffect(() => {
76
- if (isSSR || prefetch !== 'viewport' || !linkRef.current) return;
77
- const observer = new IntersectionObserver(
78
- (entries) => {
79
- if (entries[0]?.isIntersecting) {
80
- handlePrefetch();
81
- observer.disconnect();
82
- }
83
- },
84
- { rootMargin: '200px' }
85
- );
86
- observer.observe(linkRef.current);
87
- return () => observer.disconnect();
88
- }, [isSSR, prefetch, handlePrefetch]);
89
-
90
- const handleClick = (e: React.MouseEvent<HTMLAnchorElement>) => {
91
- if (rest.onClick) rest.onClick(e);
92
- if (e.defaultPrevented) return;
93
- if (target === '_blank' || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
94
- e.preventDefault();
95
- const navFn = shouldReplace ? replace : push;
96
- navFn(href, { scroll });
97
- };
98
-
99
- return (
100
- <a
101
- ref={linkRef}
102
- href={href}
103
- className={computedClassName}
104
- data-active={isPartialActive || undefined}
105
- data-exact-active={isExactActive || undefined}
106
- onClick={isSSR ? undefined : handleClick}
107
- onMouseEnter={isSSR || prefetch !== 'hover' ? undefined : handlePrefetch}
108
- onFocus={isSSR || prefetch !== 'hover' ? undefined : handlePrefetch}
109
- target={target}
110
- {...rest}
111
- >
112
- {children}
113
- </a>
114
- );
115
- };
116
- return Link;
117
- }