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.
- package/CHANGELOG.md +31 -0
- package/LICENSE +21 -0
- package/README.md +210 -199
- package/dist/index.cjs +768 -792
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +126 -19
- package/dist/index.d.ts +126 -19
- package/dist/index.js +748 -784
- package/dist/index.js.map +1 -1
- package/dist/plugin.cjs +927 -0
- package/dist/plugin.cjs.map +1 -0
- package/dist/plugin.d.cts +18 -0
- package/dist/plugin.d.ts +18 -0
- package/dist/plugin.js +894 -0
- package/dist/plugin.js.map +1 -0
- package/dist/ssg.cjs +637 -0
- package/dist/ssg.cjs.map +1 -0
- package/dist/ssg.d.cts +191 -0
- package/dist/ssg.d.ts +191 -0
- package/dist/ssg.js +585 -0
- package/dist/ssg.js.map +1 -0
- package/dist/types-BT6YsBGO.d.cts +143 -0
- package/dist/types-BT6YsBGO.d.ts +143 -0
- package/package.json +58 -57
- package/dist/client-BBLXpllK.d.ts +0 -487
- package/dist/client-C0av_vTZ.d.cts +0 -487
- package/dist/client.cjs +0 -850
- package/dist/client.cjs.map +0 -1
- package/dist/client.d.cts +0 -5
- package/dist/client.d.ts +0 -5
- package/dist/client.js +0 -816
- package/dist/client.js.map +0 -1
- package/dist/image.cjs +0 -25
- package/dist/image.cjs.map +0 -1
- package/dist/image.d.cts +0 -1
- package/dist/image.d.ts +0 -1
- package/dist/image.js +0 -3
- package/dist/image.js.map +0 -1
- package/dist/plugin/index.cjs +0 -1550
- package/dist/plugin/index.cjs.map +0 -1
- package/dist/plugin/index.d.cts +0 -131
- package/dist/plugin/index.d.ts +0 -131
- package/dist/plugin/index.js +0 -1490
- package/dist/plugin/index.js.map +0 -1
- package/dist/serialization-xKcOESDh.d.cts +0 -122
- package/dist/serialization-xKcOESDh.d.ts +0 -122
- package/dist/server.cjs +0 -547
- package/dist/server.cjs.map +0 -1
- package/dist/server.d.cts +0 -148
- package/dist/server.d.ts +0 -148
- package/dist/server.js +0 -493
- 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
|
-
|
|
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
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
- 🔗 **
|
|
13
|
-
-
|
|
14
|
-
-
|
|
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
|
|
41
|
-
import react from
|
|
42
|
-
import { olova } from
|
|
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: [
|
|
35
|
+
plugins: [
|
|
36
|
+
react(),
|
|
37
|
+
olova({
|
|
38
|
+
appDir: "src/app", // Your app directory
|
|
39
|
+
}),
|
|
40
|
+
],
|
|
46
41
|
});
|
|
47
42
|
```
|
|
48
43
|
|
|
49
|
-
### 2. Create
|
|
44
|
+
### 2. Create your app structure
|
|
50
45
|
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
80
|
+
## File Conventions
|
|
68
81
|
|
|
69
|
-
|
|
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
|
-
|
|
73
|
-
|
|
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
|
-
###
|
|
99
|
+
### Dynamic Routes
|
|
84
100
|
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
###
|
|
106
|
+
### Catch-all Routes
|
|
97
107
|
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
|
|
108
|
+
```
|
|
109
|
+
app/docs/[...slug]/page.tsx → /docs/*
|
|
110
|
+
```
|
|
101
111
|
|
|
102
|
-
|
|
103
|
-
title: 'Home',
|
|
104
|
-
description: 'Welcome to my site',
|
|
105
|
-
};
|
|
112
|
+
### Route Groups
|
|
106
113
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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
|
-
##
|
|
121
|
+
## Components
|
|
118
122
|
|
|
119
|
-
### Link
|
|
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/
|
|
126
|
-
<Link href="/dashboard" replace>Dashboard</Link>
|
|
131
|
+
<Link href="/blog/my-post" replace>Blog Post</Link>
|
|
127
132
|
```
|
|
128
133
|
|
|
129
|
-
###
|
|
134
|
+
### NavLink
|
|
135
|
+
|
|
136
|
+
Navigation link with active state:
|
|
130
137
|
|
|
131
138
|
```tsx
|
|
132
|
-
import {
|
|
139
|
+
import { NavLink } from "olova";
|
|
133
140
|
|
|
134
|
-
|
|
135
|
-
|
|
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
|
-
|
|
146
|
+
## Hooks
|
|
147
|
+
|
|
148
|
+
### usePathname
|
|
149
|
+
|
|
150
|
+
Get the current pathname:
|
|
146
151
|
|
|
147
152
|
```tsx
|
|
148
|
-
import {
|
|
153
|
+
import { usePathname } from "olova";
|
|
149
154
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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
|
-
###
|
|
161
|
+
### useParams
|
|
162
|
+
|
|
163
|
+
Access route parameters:
|
|
158
164
|
|
|
159
165
|
```tsx
|
|
160
|
-
import {
|
|
166
|
+
import { useParams } from "olova";
|
|
161
167
|
|
|
162
|
-
function
|
|
163
|
-
const
|
|
164
|
-
return <
|
|
168
|
+
function BlogPost() {
|
|
169
|
+
const { slug } = useParams<{ slug: string }>();
|
|
170
|
+
return <div>Reading: {slug}</div>;
|
|
165
171
|
}
|
|
166
172
|
```
|
|
167
173
|
|
|
168
|
-
### useSearchParams
|
|
174
|
+
### useSearchParams
|
|
175
|
+
|
|
176
|
+
Access URL search parameters:
|
|
169
177
|
|
|
170
178
|
```tsx
|
|
171
|
-
import { useSearchParams } from
|
|
179
|
+
import { useSearchParams } from "olova";
|
|
172
180
|
|
|
173
181
|
function SearchPage() {
|
|
174
182
|
const searchParams = useSearchParams();
|
|
175
|
-
const query = searchParams.get(
|
|
183
|
+
const query = searchParams.get("q");
|
|
176
184
|
return <div>Searching for: {query}</div>;
|
|
177
185
|
}
|
|
178
186
|
```
|
|
179
187
|
|
|
180
|
-
|
|
188
|
+
### useRouter
|
|
181
189
|
|
|
182
|
-
|
|
190
|
+
Programmatic navigation:
|
|
183
191
|
|
|
184
192
|
```tsx
|
|
185
|
-
|
|
193
|
+
import { useRouter } from "olova";
|
|
194
|
+
|
|
195
|
+
function Component() {
|
|
196
|
+
const router = useRouter();
|
|
186
197
|
|
|
187
|
-
|
|
188
|
-
|
|
198
|
+
return (
|
|
199
|
+
<button onClick={() => router.push("/dashboard")}>Go to Dashboard</button>
|
|
200
|
+
);
|
|
189
201
|
}
|
|
190
202
|
```
|
|
191
203
|
|
|
192
|
-
|
|
204
|
+
Router methods:
|
|
193
205
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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
|
-
##
|
|
213
|
+
## Page Props
|
|
214
|
+
|
|
215
|
+
Pages receive `params` and `searchParams`:
|
|
203
216
|
|
|
204
217
|
```tsx
|
|
205
|
-
|
|
218
|
+
// app/blog/[slug]/page.tsx
|
|
219
|
+
interface PageProps {
|
|
220
|
+
params: { slug: string };
|
|
221
|
+
searchParams: Record<string, string>;
|
|
222
|
+
}
|
|
206
223
|
|
|
207
|
-
function
|
|
224
|
+
export default function BlogPost({ params, searchParams }: PageProps) {
|
|
208
225
|
return (
|
|
209
|
-
|
|
210
|
-
<
|
|
211
|
-
|
|
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
|
-
|
|
233
|
+
## Layout Props
|
|
234
|
+
|
|
235
|
+
Layouts receive `children` and `params`:
|
|
221
236
|
|
|
222
237
|
```tsx
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
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
|
-
##
|
|
254
|
+
## Error Handling
|
|
234
255
|
|
|
235
|
-
|
|
236
|
-
import { defineConfig } from 'olova';
|
|
256
|
+
Create `error.tsx` to handle errors in a route segment:
|
|
237
257
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
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
|
-
##
|
|
276
|
+
## Loading States
|
|
274
277
|
|
|
275
|
-
|
|
278
|
+
Create `loading.tsx` for loading UI:
|
|
276
279
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
-
|
|
283
|
-
-
|
|
284
|
-
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
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
|
-
|
|
293
|
+
## Plugin Options
|
|
294
|
+
|
|
295
|
+
```ts
|
|
296
|
+
olova({
|
|
297
|
+
// Directory containing routes (default: 'src/app')
|
|
298
|
+
appDir: "src/app",
|
|
292
299
|
|
|
293
|
-
|
|
300
|
+
// File extensions to look for (default: ['.tsx', '.ts', '.jsx', '.js'])
|
|
301
|
+
extensions: [".tsx", ".ts"],
|
|
294
302
|
|
|
295
|
-
|
|
303
|
+
// Base URL path (default: '/')
|
|
304
|
+
basePath: "",
|
|
296
305
|
|
|
297
|
-
|
|
306
|
+
// Enable trailing slashes (default: false)
|
|
307
|
+
trailingSlash: false,
|
|
308
|
+
});
|
|
309
|
+
```
|
|
298
310
|
|
|
299
|
-
|
|
300
|
-
- `renderToStringWithFlight` - SSG with Flight payload
|
|
301
|
-
- `generateHtmlShell` - HTML shell generation
|
|
302
|
-
- `createStreamingHeaders` - Streaming response headers
|
|
311
|
+
## TypeScript
|
|
303
312
|
|
|
304
|
-
|
|
313
|
+
Olova is fully typed. Import types for your pages and layouts:
|
|
305
314
|
|
|
306
|
-
|
|
315
|
+
```tsx
|
|
316
|
+
import type { PageProps, LayoutProps, ErrorProps, LoadingProps } from "olova";
|
|
317
|
+
```
|
|
307
318
|
|
|
308
319
|
## License
|
|
309
320
|
|
|
310
|
-
MIT
|
|
321
|
+
MIT © sera
|