olova 2.0.53 → 2.0.55

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 (52) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/LICENSE +21 -0
  3. package/README.md +210 -199
  4. package/dist/index.cjs +768 -792
  5. package/dist/index.cjs.map +1 -1
  6. package/dist/index.d.cts +126 -19
  7. package/dist/index.d.ts +126 -19
  8. package/dist/index.js +748 -784
  9. package/dist/index.js.map +1 -1
  10. package/dist/plugin.cjs +927 -0
  11. package/dist/plugin.cjs.map +1 -0
  12. package/dist/plugin.d.cts +18 -0
  13. package/dist/plugin.d.ts +18 -0
  14. package/dist/plugin.js +894 -0
  15. package/dist/plugin.js.map +1 -0
  16. package/dist/ssg.cjs +637 -0
  17. package/dist/ssg.cjs.map +1 -0
  18. package/dist/ssg.d.cts +191 -0
  19. package/dist/ssg.d.ts +191 -0
  20. package/dist/ssg.js +585 -0
  21. package/dist/ssg.js.map +1 -0
  22. package/dist/types-BT6YsBGO.d.cts +143 -0
  23. package/dist/types-BT6YsBGO.d.ts +143 -0
  24. package/package.json +58 -57
  25. package/dist/client-BBLXpllK.d.ts +0 -487
  26. package/dist/client-C0av_vTZ.d.cts +0 -487
  27. package/dist/client.cjs +0 -850
  28. package/dist/client.cjs.map +0 -1
  29. package/dist/client.d.cts +0 -5
  30. package/dist/client.d.ts +0 -5
  31. package/dist/client.js +0 -816
  32. package/dist/client.js.map +0 -1
  33. package/dist/image.cjs +0 -25
  34. package/dist/image.cjs.map +0 -1
  35. package/dist/image.d.cts +0 -1
  36. package/dist/image.d.ts +0 -1
  37. package/dist/image.js +0 -3
  38. package/dist/image.js.map +0 -1
  39. package/dist/plugin/index.cjs +0 -1550
  40. package/dist/plugin/index.cjs.map +0 -1
  41. package/dist/plugin/index.d.cts +0 -131
  42. package/dist/plugin/index.d.ts +0 -131
  43. package/dist/plugin/index.js +0 -1490
  44. package/dist/plugin/index.js.map +0 -1
  45. package/dist/serialization-xKcOESDh.d.cts +0 -122
  46. package/dist/serialization-xKcOESDh.d.ts +0 -122
  47. package/dist/server.cjs +0 -547
  48. package/dist/server.cjs.map +0 -1
  49. package/dist/server.d.cts +0 -148
  50. package/dist/server.d.ts +0 -148
  51. package/dist/server.js +0 -493
  52. package/dist/server.js.map +0 -1
package/CHANGELOG.md ADDED
@@ -0,0 +1,31 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.0] - 2026-01-08
9
+
10
+ ### Added
11
+
12
+ - Initial release of Olova
13
+ - File-based routing with Next.js App Router conventions
14
+ - `OlovaRouter` component for rendering routes
15
+ - Vite plugin for automatic route generation
16
+ - React hooks: `usePathname`, `useParams`, `useSearchParams`, `useRouter`
17
+ - `Link` and `NavLink` components for client-side navigation
18
+ - Error boundaries with `error.tsx` support
19
+ - Loading states with `loading.tsx` support
20
+ - 404 handling with `not-found.tsx` support
21
+ - Dynamic routes with `[slug]` syntax
22
+ - Catch-all routes with `[...slug]` syntax
23
+ - Route groups with `(group)` syntax
24
+ - Nested layouts support
25
+ - TypeScript support with full type definitions
26
+ - CLI for build commands
27
+
28
+ ### Known Limitations
29
+
30
+ - SSG pre-rendering is not yet fully implemented
31
+ - Parallel routes are experimental
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 sera
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,34 +1,24 @@
1
1
  # Olova
2
2
 
3
- A Next.js-style React framework built on Vite with SSG, SSR, streaming, and progressive hydration.
3
+ > Next.js-style file-based routing for Vite + React
4
+
5
+ Olova brings the intuitive file-based routing conventions from Next.js App Router to your Vite + React applications. Build modern web apps with nested layouts, dynamic routes, loading states, and error boundaries - all through your file structure.
4
6
 
5
7
  ## Features
6
8
 
7
- - 🚀 **File-based routing** - Next.js-style pages directory
8
- - **Vite-powered** - Lightning fast HMR and builds
9
- - 📄 **SSG & SSR** - Static generation and server-side rendering
10
- - 🌊 **Streaming SSR** - React 18 streaming with Suspense
11
- - 💧 **Progressive Hydration** - Flight format for efficient hydration
12
- - 🔗 **Link Prefetching** - Automatic route prefetching
13
- - 📱 **Dynamic Routes** - `[slug]`, `[...slug]`, `[[...slug]]` patterns
14
- - 🎯 **Route Groups** - `(group)` folders for organization
9
+ - 📁 **File-based routing** - Create routes by adding files to your `app` directory
10
+ - 🏗️ **Nested layouts** - Share UI between routes with layout components
11
+ - 🔄 **Dynamic routes** - Support for `[slug]`, `[...catchAll]`, and route groups `(group)`
12
+ - **Loading states** - Per-route loading UI with `loading.tsx`
13
+ - **Error boundaries** - Per-route error handling with `error.tsx`
14
+ - 🔗 **Client-side navigation** - Seamless SPA navigation with `Link` component
15
+ - 🪝 **React hooks** - `usePathname`, `useParams`, `useSearchParams`, `useRouter`
16
+ - **Vite plugin** - Automatic route generation at build time
15
17
 
16
18
  ## Installation
17
19
 
18
20
  ```bash
19
21
  npm install olova
20
- # or
21
- yarn add olova
22
- # or
23
- pnpm add olova
24
- ```
25
-
26
- ### Peer Dependencies
27
-
28
- Olova requires the following peer dependencies:
29
-
30
- ```bash
31
- npm install react react-dom vite
32
22
  ```
33
23
 
34
24
  ## Quick Start
@@ -37,274 +27,295 @@ npm install react react-dom vite
37
27
 
38
28
  ```ts
39
29
  // vite.config.ts
40
- import { defineConfig } from 'vite';
41
- import react from '@vitejs/plugin-react';
42
- import { olova } from 'olova';
30
+ import { defineConfig } from "vite";
31
+ import react from "@vitejs/plugin-react";
32
+ import { olova } from "olova/plugin";
43
33
 
44
34
  export default defineConfig({
45
- plugins: [react(), olova()],
35
+ plugins: [
36
+ react(),
37
+ olova({
38
+ appDir: "src/app", // Your app directory
39
+ }),
40
+ ],
46
41
  });
47
42
  ```
48
43
 
49
- ### 2. Create Olova Config (Optional)
44
+ ### 2. Create your app structure
50
45
 
51
- ```ts
52
- // olova.config.ts
53
- import { defineConfig } from 'olova';
46
+ ```
47
+ src/app/
48
+ ├── layout.tsx # Root layout
49
+ ├── page.tsx # Home page (/)
50
+ ├── loading.tsx # Loading state
51
+ ├── error.tsx # Error boundary
52
+ ├── about/
53
+ │ └── page.tsx # About page (/about)
54
+ ├── blog/
55
+ │ ├── layout.tsx # Blog layout
56
+ │ ├── page.tsx # Blog index (/blog)
57
+ │ └── [slug]/
58
+ │ └── page.tsx # Blog post (/blog/:slug)
59
+ └── (marketing)/
60
+ └── pricing/
61
+ └── page.tsx # Pricing (/pricing) - grouped route
62
+ ```
54
63
 
55
- export default defineConfig({
56
- // Enable SSG globally
57
- ssg: {
58
- enabled: true,
59
- },
60
- // Configure experimental features
61
- experimental: {
62
- prefetch: true,
63
- },
64
- });
64
+ ### 3. Set up the router
65
+
66
+ ```tsx
67
+ // src/main.tsx
68
+ import { StrictMode } from "react";
69
+ import { createRoot } from "react-dom/client";
70
+ import { OlovaRouter } from "olova";
71
+ import { routes } from "virtual:olova-routes";
72
+
73
+ createRoot(document.getElementById("root")!).render(
74
+ <StrictMode>
75
+ <OlovaRouter routes={routes} />
76
+ </StrictMode>
77
+ );
65
78
  ```
66
79
 
67
- ### 3. Create Pages
80
+ ## File Conventions
68
81
 
69
- Create a `src` directory with page files:
82
+ | File | Purpose |
83
+ | --------------- | ------------------------------------------------------ |
84
+ | `page.tsx` | UI for the route (required for route to be accessible) |
85
+ | `layout.tsx` | Shared layout wrapping child routes |
86
+ | `loading.tsx` | Loading UI shown while page loads |
87
+ | `error.tsx` | Error UI shown when errors occur |
88
+ | `not-found.tsx` | 404 UI for the route |
89
+
90
+ ## Route Patterns
91
+
92
+ ### Static Routes
70
93
 
71
94
  ```
72
- src/
73
- ├── layout.tsx # Root layout
74
- ├── page.tsx # Home page (/)
75
- ├── about/
76
- │ └── page.tsx # About page (/about)
77
- └── blog/
78
- ├── page.tsx # Blog index (/blog)
79
- └── [slug]/
80
- └── page.tsx # Blog post (/blog/:slug)
95
+ app/about/page.tsx → /about
96
+ app/contact/page.tsx /contact
81
97
  ```
82
98
 
83
- ### 4. Create Layout
99
+ ### Dynamic Routes
84
100
 
85
- ```tsx
86
- // src/layout.tsx
87
- export default function Layout({ children }: { children: React.ReactNode }) {
88
- return (
89
- <html>
90
- <body>{children}</body>
91
- </html>
92
- );
93
- }
101
+ ```
102
+ app/blog/[slug]/page.tsx → /blog/:slug
103
+ app/users/[id]/page.tsx /users/:id
94
104
  ```
95
105
 
96
- ### 5. Create a Page
106
+ ### Catch-all Routes
97
107
 
98
- ```tsx
99
- // src/page.tsx
100
- import { Link } from 'olova';
108
+ ```
109
+ app/docs/[...slug]/page.tsx → /docs/*
110
+ ```
101
111
 
102
- export const metadata = {
103
- title: 'Home',
104
- description: 'Welcome to my site',
105
- };
112
+ ### Route Groups
106
113
 
107
- export default function HomePage() {
108
- return (
109
- <div>
110
- <h1>Welcome</h1>
111
- <Link href="/about">About</Link>
112
- </div>
113
- );
114
- }
114
+ Route groups `(folder)` organize files without affecting the URL:
115
+
116
+ ```
117
+ app/(marketing)/pricing/page.tsx → /pricing
118
+ app/(marketing)/about/page.tsx /about
115
119
  ```
116
120
 
117
- ## Routing
121
+ ## Components
118
122
 
119
- ### Link Component
123
+ ### Link
124
+
125
+ Client-side navigation with prefetching:
120
126
 
121
127
  ```tsx
122
128
  import { Link } from 'olova';
123
129
 
124
130
  <Link href="/about">About</Link>
125
- <Link href="/blog/hello" prefetch={false}>Blog Post</Link>
126
- <Link href="/dashboard" replace>Dashboard</Link>
131
+ <Link href="/blog/my-post" replace>Blog Post</Link>
127
132
  ```
128
133
 
129
- ### useRouter Hook
134
+ ### NavLink
135
+
136
+ Navigation link with active state:
130
137
 
131
138
  ```tsx
132
- import { useRouter } from 'olova';
139
+ import { NavLink } from "olova";
133
140
 
134
- function MyComponent() {
135
- const router = useRouter();
136
-
137
- const handleClick = () => {
138
- router.push('/dashboard');
139
- };
140
-
141
- return <button onClick={handleClick}>Go to Dashboard</button>;
142
- }
141
+ <NavLink href="/dashboard" activeClassName="text-blue-500" exact>
142
+ Dashboard
143
+ </NavLink>;
143
144
  ```
144
145
 
145
- ### useParams Hook
146
+ ## Hooks
147
+
148
+ ### usePathname
149
+
150
+ Get the current pathname:
146
151
 
147
152
  ```tsx
148
- import { useParams } from 'olova';
153
+ import { usePathname } from "olova";
149
154
 
150
- // In /blog/[slug]/page.tsx
151
- function BlogPost() {
152
- const { slug } = useParams();
153
- return <h1>Post: {slug}</h1>;
155
+ function Component() {
156
+ const pathname = usePathname();
157
+ return <div>Current path: {pathname}</div>;
154
158
  }
155
159
  ```
156
160
 
157
- ### usePathname Hook
161
+ ### useParams
162
+
163
+ Access route parameters:
158
164
 
159
165
  ```tsx
160
- import { usePathname } from 'olova';
166
+ import { useParams } from "olova";
161
167
 
162
- function Navigation() {
163
- const pathname = usePathname();
164
- return <nav>Current: {pathname}</nav>;
168
+ function BlogPost() {
169
+ const { slug } = useParams<{ slug: string }>();
170
+ return <div>Reading: {slug}</div>;
165
171
  }
166
172
  ```
167
173
 
168
- ### useSearchParams Hook
174
+ ### useSearchParams
175
+
176
+ Access URL search parameters:
169
177
 
170
178
  ```tsx
171
- import { useSearchParams } from 'olova';
179
+ import { useSearchParams } from "olova";
172
180
 
173
181
  function SearchPage() {
174
182
  const searchParams = useSearchParams();
175
- const query = searchParams.get('q');
183
+ const query = searchParams.get("q");
176
184
  return <div>Searching for: {query}</div>;
177
185
  }
178
186
  ```
179
187
 
180
- ## Static Generation
188
+ ### useRouter
181
189
 
182
- Mark pages for static generation with the `"use static"` directive:
190
+ Programmatic navigation:
183
191
 
184
192
  ```tsx
185
- "use static";
193
+ import { useRouter } from "olova";
194
+
195
+ function Component() {
196
+ const router = useRouter();
186
197
 
187
- export default function AboutPage() {
188
- return <h1>About Us</h1>;
198
+ return (
199
+ <button onClick={() => router.push("/dashboard")}>Go to Dashboard</button>
200
+ );
189
201
  }
190
202
  ```
191
203
 
192
- Or enable globally in `olova.config.ts`:
204
+ Router methods:
193
205
 
194
- ```ts
195
- export default defineConfig({
196
- ssg: {
197
- enabled: true,
198
- },
199
- });
200
- ```
206
+ - `push(url)` - Navigate to URL
207
+ - `replace(url)` - Navigate without adding to history
208
+ - `back()` - Go back
209
+ - `forward()` - Go forward
210
+ - `refresh()` - Refresh current route
211
+ - `prefetch(url)` - Prefetch a route
201
212
 
202
- ## Head Management
213
+ ## Page Props
214
+
215
+ Pages receive `params` and `searchParams`:
203
216
 
204
217
  ```tsx
205
- import { Head } from 'olova';
218
+ // app/blog/[slug]/page.tsx
219
+ interface PageProps {
220
+ params: { slug: string };
221
+ searchParams: Record<string, string>;
222
+ }
206
223
 
207
- function MyPage() {
224
+ export default function BlogPost({ params, searchParams }: PageProps) {
208
225
  return (
209
- <>
210
- <Head>
211
- <title>My Page</title>
212
- <meta name="description" content="Page description" />
213
- </Head>
214
- <div>Content</div>
215
- </>
226
+ <article>
227
+ <h1>Post: {params.slug}</h1>
228
+ </article>
216
229
  );
217
230
  }
218
231
  ```
219
232
 
220
- Or use the `metadata` export:
233
+ ## Layout Props
234
+
235
+ Layouts receive `children` and `params`:
221
236
 
222
237
  ```tsx
223
- export const metadata = {
224
- title: 'My Page',
225
- description: 'Page description',
226
- openGraph: {
227
- title: 'My Page',
228
- description: 'Page description',
229
- },
230
- };
238
+ // app/blog/layout.tsx
239
+ interface LayoutProps {
240
+ children: React.ReactNode;
241
+ params: { slug?: string };
242
+ }
243
+
244
+ export default function BlogLayout({ children }: LayoutProps) {
245
+ return (
246
+ <div className="blog-layout">
247
+ <nav>Blog Navigation</nav>
248
+ <main>{children}</main>
249
+ </div>
250
+ );
251
+ }
231
252
  ```
232
253
 
233
- ## Configuration Options
254
+ ## Error Handling
234
255
 
235
- ```ts
236
- import { defineConfig } from 'olova';
256
+ Create `error.tsx` to handle errors in a route segment:
237
257
 
238
- export default defineConfig({
239
- // Output directory
240
- outDir: '.olova/dist',
241
-
242
- // Base path for the app
243
- basePath: '/',
244
-
245
- // Enable trailing slashes
246
- trailingSlash: false,
247
-
248
- // SSG configuration
249
- ssg: {
250
- enabled: false,
251
- prerender: ['/about', '/contact'],
252
- },
253
-
254
- // Experimental features
255
- experimental: {
256
- prefetch: true,
257
- serverComponents: false,
258
- },
259
-
260
- // Static generation settings
261
- staticGeneration: {
262
- timeout: 30000,
263
- fallback: 'blocking',
264
- },
265
-
266
- // Custom Vite config
267
- vite: {
268
- // Vite options
269
- },
270
- });
258
+ ```tsx
259
+ // app/blog/error.tsx
260
+ interface ErrorProps {
261
+ error: Error;
262
+ reset: () => void;
263
+ }
264
+
265
+ export default function BlogError({ error, reset }: ErrorProps) {
266
+ return (
267
+ <div>
268
+ <h2>Something went wrong!</h2>
269
+ <p>{error.message}</p>
270
+ <button onClick={reset}>Try again</button>
271
+ </div>
272
+ );
273
+ }
271
274
  ```
272
275
 
273
- ## Exports
276
+ ## Loading States
274
277
 
275
- ### Main Entry (`olova`)
278
+ Create `loading.tsx` for loading UI:
276
279
 
277
- - `olova` - Vite plugin
278
- - `defineConfig` - Configuration helper
279
- - `Link` - Navigation component
280
- - `Router` - Router component
281
- - `useRouter` - Router hook
282
- - `useParams` - Params hook
283
- - `usePathname` - Pathname hook
284
- - `useSearchParams` - Search params hook
285
- - `navigate` - Programmatic navigation
286
- - `replace` - Replace navigation
287
- - `Head` - Head management component
288
- - `HeadProvider` - Head context provider
289
- - `usePageData` - Page data hook
280
+ ```tsx
281
+ // app/blog/loading.tsx
282
+ export default function BlogLoading() {
283
+ return (
284
+ <div className="animate-pulse">
285
+ <div className="h-8 bg-gray-200 rounded w-3/4 mb-4" />
286
+ <div className="h-4 bg-gray-200 rounded w-full mb-2" />
287
+ <div className="h-4 bg-gray-200 rounded w-5/6" />
288
+ </div>
289
+ );
290
+ }
291
+ ```
290
292
 
291
- ### Client Entry (`olova/client`)
293
+ ## Plugin Options
294
+
295
+ ```ts
296
+ olova({
297
+ // Directory containing routes (default: 'src/app')
298
+ appDir: "src/app",
292
299
 
293
- Client-side hydration and routing utilities.
300
+ // File extensions to look for (default: ['.tsx', '.ts', '.jsx', '.js'])
301
+ extensions: [".tsx", ".ts"],
294
302
 
295
- ### Server Entry (`olova/server`)
303
+ // Base URL path (default: '/')
304
+ basePath: "",
296
305
 
297
- Server-side rendering and streaming utilities:
306
+ // Enable trailing slashes (default: false)
307
+ trailingSlash: false,
308
+ });
309
+ ```
298
310
 
299
- - `renderToStream` - Streaming SSR
300
- - `renderToStringWithFlight` - SSG with Flight payload
301
- - `generateHtmlShell` - HTML shell generation
302
- - `createStreamingHeaders` - Streaming response headers
311
+ ## TypeScript
303
312
 
304
- ### Plugin Entry (`olova/plugin`)
313
+ Olova is fully typed. Import types for your pages and layouts:
305
314
 
306
- Direct access to the Vite plugin.
315
+ ```tsx
316
+ import type { PageProps, LayoutProps, ErrorProps, LoadingProps } from "olova";
317
+ ```
307
318
 
308
319
  ## License
309
320
 
310
- MIT
321
+ MIT © sera