reroute-js 0.2.3 → 0.3.1
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/README.md +24 -7
- package/_/basic/package.json +2 -1
- package/_/blog/package.json +2 -1
- package/_/store/package.json +2 -1
- package/_/store/src/client/components/ProductCard.tsx +6 -3
- package/_/store/src/client/lib/api.ts +66 -1
- package/_/store/src/client/routes/index.tsx +3 -3
- package/_/store/src/client/routes/products/[id].tsx +7 -2
- package/_/store/src/index.ts +1 -0
- package/cli/bin.d.ts +9 -1
- package/cli/bin.js +28 -23
- package/cli/bin.js.map +5 -5
- package/cli/index.d.ts +9 -0
- package/cli/index.js +12 -46
- package/cli/index.js.map +3 -3
- package/cli/src/cli.d.ts +9 -1
- package/cli/src/commands/build.d.ts +9 -1
- package/cli/src/commands/dev.d.ts +9 -1
- package/cli/src/commands/gen.d.ts +9 -1
- package/cli/src/commands/init.d.ts +9 -1
- package/cli/src/libs/index.d.ts +9 -0
- package/cli/src/libs/tailwind.d.ts +9 -34
- package/cli/src/libs/tailwind.d.ts.map +1 -1
- package/core/index.d.ts +9 -0
- package/core/index.js +24 -3
- package/core/index.js.map +4 -4
- package/core/src/bundler/hash.d.ts +9 -0
- package/core/src/bundler/index.d.ts +9 -0
- package/core/src/bundler/transpile.d.ts +9 -0
- package/core/src/bundler/transpile.d.ts.map +1 -1
- package/core/src/content/discovery.d.ts +9 -0
- package/core/src/content/index.d.ts +9 -0
- package/core/src/content/metadata.d.ts +9 -0
- package/core/src/content/registry.d.ts +9 -0
- package/core/src/index.d.ts +9 -0
- package/core/src/ssr/data.d.ts +9 -0
- package/core/src/ssr/index.d.ts +9 -0
- package/core/src/ssr/modules.d.ts +9 -0
- package/core/src/ssr/render.d.ts +9 -0
- package/core/src/ssr/seed.d.ts +9 -0
- package/core/src/template/html.d.ts +9 -0
- package/core/src/template/index.d.ts +9 -0
- package/core/src/types.d.ts +9 -0
- package/core/src/utils/cache.d.ts +9 -0
- package/core/src/utils/compression.d.ts +9 -0
- package/core/src/utils/index.d.ts +9 -0
- package/core/src/utils/mime.d.ts +9 -0
- package/core/src/utils/path.d.ts +9 -0
- package/elysia/index.d.ts +9 -0
- package/elysia/index.js +419 -98
- package/elysia/index.js.map +12 -10
- package/elysia/src/index.d.ts +9 -0
- package/elysia/src/{utils → libs}/http.d.ts +9 -0
- package/elysia/src/libs/http.d.ts.map +1 -0
- package/elysia/src/libs/image.d.ts +38 -0
- package/elysia/src/libs/image.d.ts.map +1 -0
- package/elysia/src/plugin.d.ts +9 -0
- package/elysia/src/plugin.d.ts.map +1 -1
- package/elysia/src/routes/artifacts.d.ts +9 -0
- package/elysia/src/routes/content.d.ts +9 -0
- package/elysia/src/routes/dev.d.ts +11 -0
- package/elysia/src/routes/dev.d.ts.map +1 -1
- package/elysia/src/routes/image.d.ts +23 -0
- package/elysia/src/routes/image.d.ts.map +1 -0
- package/elysia/src/routes/ssr.d.ts +11 -0
- package/elysia/src/routes/ssr.d.ts.map +1 -1
- package/elysia/src/routes/static.d.ts +9 -0
- package/elysia/src/routes/static.d.ts.map +1 -1
- package/elysia/src/types.d.ts +13 -0
- package/elysia/src/types.d.ts.map +1 -1
- package/package.json +5 -4
- package/react/index.d.ts +9 -0
- package/react/index.js +223 -5
- package/react/index.js.map +4 -3
- package/react/src/components/ContentRoute.d.ts +9 -0
- package/react/src/components/Image.d.ts +53 -0
- package/react/src/components/Image.d.ts.map +1 -0
- package/react/src/components/Link.d.ts +9 -0
- package/react/src/components/Outlet.d.ts +9 -0
- package/react/src/components/index.d.ts +10 -0
- package/react/src/components/index.d.ts.map +1 -1
- package/react/src/hooks/index.d.ts +9 -0
- package/react/src/hooks/useContent.d.ts +9 -0
- package/react/src/hooks/useData.d.ts +9 -0
- package/react/src/hooks/useNavigate.d.ts +9 -0
- package/react/src/hooks/useParams.d.ts +9 -0
- package/react/src/hooks/useRouter.d.ts +9 -0
- package/react/src/hooks/useSearchParams.d.ts +9 -0
- package/react/src/index.d.ts +9 -0
- package/react/src/providers/ContentProvider.d.ts +9 -0
- package/react/src/providers/RerouteProvider.d.ts +9 -0
- package/react/src/providers/RouterProvider.d.ts +9 -0
- package/react/src/providers/index.d.ts +9 -0
- package/react/src/types/any.d.ts +9 -0
- package/react/src/types/index.d.ts +9 -0
- package/react/src/types/router.d.ts +9 -0
- package/react/src/utils/content.d.ts +9 -0
- package/react/src/utils/head.d.ts +9 -0
- package/react/src/utils/index.d.ts +9 -0
- package/elysia/src/utils/http.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -34,6 +34,7 @@ Reroute is a dead-simple file-based routing framework for building full-stack Re
|
|
|
34
34
|
- `useContent()` - Load and manage content collections with sorting
|
|
35
35
|
- 🧩 **Components**:
|
|
36
36
|
- `<Link>` - Client-side navigation with automatic prefetching
|
|
37
|
+
- `<Image>` - Optimized images with automatic format negotiation and lazy loading
|
|
37
38
|
- `<Outlet>` - Render nested route components
|
|
38
39
|
- `<ContentRoute>` - Dynamic content rendering with metadata injection
|
|
39
40
|
- 🎭 **Providers**:
|
|
@@ -44,6 +45,20 @@ Reroute is a dead-simple file-based routing framework for building full-stack Re
|
|
|
44
45
|
- `useData()` - Read route-level SSR data without loaders
|
|
45
46
|
- `export const ssr = { data() {} }` - Route data function executed on server
|
|
46
47
|
|
|
48
|
+
### 🖼️ Image Optimization
|
|
49
|
+
- 🎨 **Format Control** - Support for auto, AVIF, WebP, JPEG, and PNG formats
|
|
50
|
+
- 📐 **Responsive Images** - Automatic srcset generation for multiple device sizes (640-3840px)
|
|
51
|
+
- ⚡ **On-Demand Processing** - Server-side image transformation using Sharp
|
|
52
|
+
- 💾 **Smart Caching** - In-memory and disk cache for optimized images (1-year cache headers)
|
|
53
|
+
- 🔍 **Lazy Loading** - Native lazy loading with IntersectionObserver fallback for older browsers
|
|
54
|
+
- ⏫ **Priority Loading** - Opt-in eager loading for above-the-fold images
|
|
55
|
+
- 🌫️ **Blur Placeholder** - Optional blur-up effect with custom blur data URL support
|
|
56
|
+
- 🎯 **Quality Control** - Configurable quality (1-100, default: 75)
|
|
57
|
+
- 🔄 **Loading States** - Built-in spinner with customization (size, color, background, custom component)
|
|
58
|
+
- 🎨 **Custom Loader** - Override default image URL generation
|
|
59
|
+
- 📏 **Callbacks** - onLoad and onError event handlers
|
|
60
|
+
- 📱 **Sizes Attribute** - Control responsive image selection with custom sizes
|
|
61
|
+
|
|
47
62
|
### 🚀 Performance Optimizations
|
|
48
63
|
- 💾 **Smart Caching** - LRU cache for files and bundles
|
|
49
64
|
- 🔗 **Link Prefetching** - Automatic content prefetching on hover/focus
|
|
@@ -113,12 +128,7 @@ cd my-app
|
|
|
113
128
|
bun dev
|
|
114
129
|
```
|
|
115
130
|
|
|
116
|
-
##
|
|
117
|
-
|
|
118
|
-
- [Examples](./examples) - Example projects to get started
|
|
119
|
-
- [GitHub](https://github.com/stewones/reroute) - Source code and issues
|
|
120
|
-
|
|
121
|
-
## 🔌 SSR Data: External Requests Without Loaders
|
|
131
|
+
## 🔌 External Requests SSR
|
|
122
132
|
|
|
123
133
|
Reroute can execute route-level data fetching on the server and inline the result for instant hydration. Define an optional `ssr.data` in a route module and read it with `useData()`:
|
|
124
134
|
|
|
@@ -144,10 +154,17 @@ export default function Page() {
|
|
|
144
154
|
```
|
|
145
155
|
|
|
146
156
|
Notes:
|
|
147
|
-
- Works with Bun + Elysia
|
|
157
|
+
- Works with Bun + Elysia, no client loaders needed for initial render
|
|
148
158
|
- Data is injected as `window.__REROUTE_DATA__` and read during hydration
|
|
149
159
|
- Use with existing content features (useContent); both can be seeded in the same page
|
|
150
160
|
|
|
161
|
+
|
|
162
|
+
## 📖 Learn More
|
|
163
|
+
|
|
164
|
+
- [Examples](./examples) - Example projects to get started
|
|
165
|
+
- [Packages](./packages) - Reroute entrypoints
|
|
166
|
+
- [GitHub](https://github.com/stewones/reroute) - Source code and issues
|
|
167
|
+
|
|
151
168
|
## 📝 License
|
|
152
169
|
|
|
153
170
|
MIT
|
package/_/basic/package.json
CHANGED
package/_/blog/package.json
CHANGED
package/_/store/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** biome-ignore-all lint/a11y/noStaticElementInteractions: who cares, this is just an example */
|
|
2
|
-
import { Link } from 'reroute-js/react';
|
|
2
|
+
import { Image, Link } from 'reroute-js/react';
|
|
3
3
|
import type { Product } from '../lib/api';
|
|
4
4
|
|
|
5
5
|
interface ProductCardProps {
|
|
@@ -14,11 +14,14 @@ export default function ProductCard({ product }: ProductCardProps) {
|
|
|
14
14
|
className='block h-full no-underline text-inherit'
|
|
15
15
|
>
|
|
16
16
|
<div className='w-full h-60 bg-gray-50 flex items-center justify-center p-4'>
|
|
17
|
-
<
|
|
17
|
+
<Image
|
|
18
18
|
src={product.image}
|
|
19
19
|
alt={product.title}
|
|
20
|
+
width={180}
|
|
21
|
+
height={180}
|
|
22
|
+
quality={75}
|
|
23
|
+
sizes='(max-width: 640px) 50vw, (max-width: 1024px) 33vw, 180px'
|
|
20
24
|
className='max-w-full max-h-full object-contain'
|
|
21
|
-
loading='lazy'
|
|
22
25
|
style={{ viewTransitionName: `product-image-${product.id}` }}
|
|
23
26
|
/>
|
|
24
27
|
</div>
|
|
@@ -3,6 +3,15 @@
|
|
|
3
3
|
|
|
4
4
|
const API_BASE_URL = 'https://fakestoreapi.com';
|
|
5
5
|
|
|
6
|
+
// In-memory cache for API responses
|
|
7
|
+
interface CacheEntry<T> {
|
|
8
|
+
data: T;
|
|
9
|
+
timestamp: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const cache = new Map<string, CacheEntry<unknown>>();
|
|
13
|
+
const CACHE_TTL = 60000; // 1 minute cache
|
|
14
|
+
|
|
6
15
|
interface Product {
|
|
7
16
|
id: number;
|
|
8
17
|
title: string;
|
|
@@ -65,11 +74,67 @@ function ensureSerializable<T>(data: T): T {
|
|
|
65
74
|
return JSON.parse(JSON.stringify(data));
|
|
66
75
|
}
|
|
67
76
|
|
|
68
|
-
//
|
|
77
|
+
// Cache helper functions
|
|
78
|
+
function getCached<T>(key: string): T | null {
|
|
79
|
+
const entry = cache.get(key) as CacheEntry<T> | undefined;
|
|
80
|
+
if (!entry) return null;
|
|
81
|
+
|
|
82
|
+
const isExpired = Date.now() - entry.timestamp > CACHE_TTL;
|
|
83
|
+
if (isExpired) {
|
|
84
|
+
cache.delete(key);
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return entry.data;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function setCache<T>(key: string, data: T): void {
|
|
92
|
+
cache.set(key, {
|
|
93
|
+
data,
|
|
94
|
+
timestamp: Date.now(),
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Generic fetch wrapper with error handling and caching
|
|
69
99
|
async function apiFetch<T>(
|
|
70
100
|
endpoint: string,
|
|
71
101
|
options?: RequestInit,
|
|
72
102
|
): Promise<T> {
|
|
103
|
+
// Check cache first (only for GET requests)
|
|
104
|
+
if (!options || !options.method || options.method === 'GET') {
|
|
105
|
+
const cacheKey = `${endpoint}:${JSON.stringify(options || {})}`;
|
|
106
|
+
const cached = getCached<T>(cacheKey);
|
|
107
|
+
if (cached) {
|
|
108
|
+
return cached;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
const response = await fetch(`${API_BASE_URL}${endpoint}`, {
|
|
113
|
+
...options,
|
|
114
|
+
headers: {
|
|
115
|
+
'Content-Type': 'application/json',
|
|
116
|
+
...options?.headers,
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
if (!response.ok) {
|
|
121
|
+
throw new Error(`API error: ${response.status} ${response.statusText}`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const data = await response.json();
|
|
125
|
+
const serialized = ensureSerializable(data);
|
|
126
|
+
|
|
127
|
+
// Cache the result
|
|
128
|
+
setCache(cacheKey, serialized);
|
|
129
|
+
|
|
130
|
+
return serialized;
|
|
131
|
+
} catch (error) {
|
|
132
|
+
console.error(`API request failed: ${endpoint}`, error);
|
|
133
|
+
throw error;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Non-GET requests - no caching
|
|
73
138
|
try {
|
|
74
139
|
const response = await fetch(`${API_BASE_URL}${endpoint}`, {
|
|
75
140
|
...options,
|
|
@@ -80,8 +80,8 @@ function HomePage() {
|
|
|
80
80
|
Lightning Fast
|
|
81
81
|
</h3>
|
|
82
82
|
<p className='text-gray-600 leading-relaxed'>
|
|
83
|
-
Built on Bun for blazing fast performance
|
|
84
|
-
|
|
83
|
+
Built on Bun for blazing fast performance, server-side rendering
|
|
84
|
+
and image caching
|
|
85
85
|
</p>
|
|
86
86
|
</div>
|
|
87
87
|
|
|
@@ -102,7 +102,7 @@ function HomePage() {
|
|
|
102
102
|
SSR Support
|
|
103
103
|
</h3>
|
|
104
104
|
<p className='text-gray-600 leading-relaxed'>
|
|
105
|
-
SEO-friendly
|
|
105
|
+
SEO-friendly with automatic hydration
|
|
106
106
|
</p>
|
|
107
107
|
</div>
|
|
108
108
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useEffect, useState } from 'react';
|
|
2
|
-
import { Link, useData, useParams } from 'reroute-js/react';
|
|
2
|
+
import { Image, Link, useData, useParams } from 'reroute-js/react';
|
|
3
3
|
import Header from '../../components/Header';
|
|
4
4
|
import ProductCard from '../../components/ProductCard';
|
|
5
5
|
import { getProduct, getProductsByCategory, type Product } from '../../lib/api';
|
|
@@ -141,9 +141,14 @@ function ProductDetailPage() {
|
|
|
141
141
|
<div className='grid grid-cols-1 lg:grid-cols-2 gap-12 mb-16'>
|
|
142
142
|
{/* Product Image */}
|
|
143
143
|
<div className='card flex items-center justify-center p-8 min-h-[500px]'>
|
|
144
|
-
<
|
|
144
|
+
<Image
|
|
145
145
|
src={product.image}
|
|
146
146
|
alt={product.title}
|
|
147
|
+
width={350}
|
|
148
|
+
height={350}
|
|
149
|
+
quality={85}
|
|
150
|
+
sizes='(max-width: 1024px) 90vw, 350px'
|
|
151
|
+
priority
|
|
147
152
|
className='max-w-full max-h-[500px] object-contain'
|
|
148
153
|
style={{ viewTransitionName: `product-image-${product.id}` }}
|
|
149
154
|
/>
|
package/_/store/src/index.ts
CHANGED
package/cli/bin.d.ts
CHANGED
package/cli/bin.js
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
// @bun
|
|
3
|
+
/**
|
|
4
|
+
* reroute-js v0.2.2
|
|
5
|
+
*
|
|
6
|
+
* @license MIT
|
|
7
|
+
* @copyright 2025 stewones <hi@stewan.io>
|
|
8
|
+
* @see https://github.com/stewones/reroute
|
|
9
|
+
*
|
|
10
|
+
* Built with Bun <3
|
|
11
|
+
*/
|
|
3
12
|
var __defProp = Object.defineProperty;
|
|
4
13
|
var __export = (target, all) => {
|
|
5
14
|
for (var name in all)
|
|
@@ -288,21 +297,17 @@ async function buildTailwind(cwd) {
|
|
|
288
297
|
});
|
|
289
298
|
});
|
|
290
299
|
}
|
|
291
|
-
function
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
return
|
|
300
|
+
async function buildTailwindIfConfigured(cwd) {
|
|
301
|
+
try {
|
|
302
|
+
if (isTailwindAvailable(cwd) && hasTailwindInput(cwd)) {
|
|
303
|
+
await buildTailwind(cwd);
|
|
304
|
+
return true;
|
|
305
|
+
}
|
|
306
|
+
return false;
|
|
307
|
+
} catch (e) {
|
|
308
|
+
console.warn("[reroute/tailwind] build failed:", e);
|
|
309
|
+
return true;
|
|
298
310
|
}
|
|
299
|
-
console.log("[reroute/tailwind] Detected Tailwind CSS v4");
|
|
300
|
-
buildTailwind(cwd).then(() => {
|
|
301
|
-
console.log("[reroute/tailwind] Built successfully");
|
|
302
|
-
}).catch((error) => {
|
|
303
|
-
console.error("[reroute/tailwind] Build failed:", error);
|
|
304
|
-
});
|
|
305
|
-
return null;
|
|
306
311
|
}
|
|
307
312
|
var init_tailwind = () => {};
|
|
308
313
|
|
|
@@ -719,14 +724,14 @@ async function gen(args) {
|
|
|
719
724
|
const watchMode = args.includes("--watch") || args.includes("-w");
|
|
720
725
|
if (!watchMode) {
|
|
721
726
|
await generate(cwd);
|
|
722
|
-
|
|
727
|
+
await buildTailwindIfConfigured(cwd);
|
|
723
728
|
return;
|
|
724
729
|
}
|
|
725
730
|
console.log("[reroute/gen] Watch mode enabled");
|
|
726
731
|
console.log("[reroute/gen] Initial generation...");
|
|
727
732
|
await generate(cwd);
|
|
728
733
|
try {
|
|
729
|
-
await
|
|
734
|
+
await buildTailwindIfConfigured(cwd);
|
|
730
735
|
} catch {}
|
|
731
736
|
const notifyReload = async (reason) => {
|
|
732
737
|
const ports = [
|
|
@@ -757,7 +762,7 @@ async function gen(args) {
|
|
|
757
762
|
console.log("[reroute/gen] Change detected, regenerating...");
|
|
758
763
|
try {
|
|
759
764
|
await generate(cwd);
|
|
760
|
-
await
|
|
765
|
+
await buildTailwindIfConfigured(cwd);
|
|
761
766
|
await notifyReload("routes change");
|
|
762
767
|
} catch (e) {
|
|
763
768
|
console.error("[reroute/gen] Error during regeneration:", e);
|
|
@@ -781,7 +786,7 @@ async function gen(args) {
|
|
|
781
786
|
clearTimeout(twDebounce);
|
|
782
787
|
twDebounce = setTimeout(async () => {
|
|
783
788
|
try {
|
|
784
|
-
await
|
|
789
|
+
await buildTailwindIfConfigured(cwd);
|
|
785
790
|
await notifyReload("client change");
|
|
786
791
|
} catch {}
|
|
787
792
|
}, 150);
|
|
@@ -819,14 +824,14 @@ async function main() {
|
|
|
819
824
|
process.exit(0);
|
|
820
825
|
}
|
|
821
826
|
if (args.length === 0 || args[0] === "--help" || args[0] === "-h") {
|
|
822
|
-
printHelp2();
|
|
827
|
+
await printHelp2();
|
|
823
828
|
process.exit(0);
|
|
824
829
|
}
|
|
825
830
|
const command = args[0];
|
|
826
831
|
if (!commands[command]) {
|
|
827
832
|
console.error(`Unknown command: ${command}`);
|
|
828
833
|
console.error("");
|
|
829
|
-
printHelp2();
|
|
834
|
+
await printHelp2();
|
|
830
835
|
process.exit(1);
|
|
831
836
|
}
|
|
832
837
|
try {
|
|
@@ -837,8 +842,8 @@ async function main() {
|
|
|
837
842
|
process.exit(1);
|
|
838
843
|
}
|
|
839
844
|
}
|
|
840
|
-
function printHelp2() {
|
|
841
|
-
console.log(
|
|
845
|
+
async function printHelp2() {
|
|
846
|
+
console.log(`Reroute v${await getVersion()}`);
|
|
842
847
|
console.log("");
|
|
843
848
|
console.log("Usage:");
|
|
844
849
|
console.log(" reroute <command> [options]");
|
|
@@ -875,4 +880,4 @@ async function getVersion() {
|
|
|
875
880
|
}
|
|
876
881
|
main();
|
|
877
882
|
|
|
878
|
-
//# debugId=
|
|
883
|
+
//# debugId=2E434D31D2F3337264756E2164756E21
|