reroute-js 0.11.5 → 0.13.0

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 (157) hide show
  1. package/README.md +278 -5
  2. package/_/basic/package.json +7 -6
  3. package/_/basic/reroute.config.ts +0 -5
  4. package/_/basic/src/index.ts +5 -2
  5. package/_/blog/package.json +7 -6
  6. package/_/blog/src/index.ts +2 -2
  7. package/_/redirects/package.json +7 -6
  8. package/_/rss/package.json +28 -0
  9. package/_/rss/reroute.config.ts +23 -0
  10. package/_/rss/tsconfig.json +25 -0
  11. package/_/sitemap/package.json +28 -0
  12. package/_/sitemap/reroute.config.ts +23 -0
  13. package/_/sitemap/tsconfig.json +25 -0
  14. package/_/store/package.json +7 -6
  15. package/_/store/src/index.ts +3 -6
  16. package/_/streaming/package.json +7 -6
  17. package/cli/bin.d.ts +1 -1
  18. package/cli/bin.js +893 -53
  19. package/cli/bin.js.map +17 -11
  20. package/cli/index.d.ts +1 -1
  21. package/cli/index.js +4 -4
  22. package/cli/index.js.map +1 -1
  23. package/cli/src/cli.d.ts +1 -1
  24. package/cli/src/commands/analyze.d.ts +1 -1
  25. package/cli/src/commands/build.d.ts +1 -1
  26. package/cli/src/commands/dev.d.ts +1 -1
  27. package/cli/src/commands/gen.d.ts +1 -1
  28. package/cli/src/commands/index.d.ts +1 -1
  29. package/cli/src/commands/init.d.ts +1 -1
  30. package/cli/src/commands/lib/command.d.ts +1 -1
  31. package/cli/src/commands/lib/env.d.ts +1 -1
  32. package/cli/src/commands/lib/index.d.ts +1 -1
  33. package/cli/src/commands/lib/log.d.ts +1 -1
  34. package/cli/src/commands/lib/markdown/availability.d.ts +1 -1
  35. package/cli/src/commands/lib/markdown/index.d.ts +1 -1
  36. package/cli/src/commands/lib/markdown/processor.d.ts +1 -1
  37. package/cli/src/commands/lib/production.d.ts +1 -1
  38. package/cli/src/commands/lib/server.d.ts +1 -1
  39. package/cli/src/commands/lib/streaming/analyzer.d.ts +1 -1
  40. package/cli/src/commands/lib/streaming/suspense.d.ts +1 -1
  41. package/cli/src/commands/lib/tailwind.d.ts +1 -1
  42. package/cli/src/commands/lib/version.d.ts +1 -1
  43. package/cli/src/commands/start.d.ts +1 -1
  44. package/cli/src/index.d.ts +1 -1
  45. package/core/index.d.ts +1 -1
  46. package/core/index.js +902 -67
  47. package/core/index.js.map +16 -11
  48. package/core/src/bundler/hash.d.ts +1 -1
  49. package/core/src/bundler/index.d.ts +1 -1
  50. package/core/src/config.d.ts +86 -2
  51. package/core/src/config.d.ts.map +1 -1
  52. package/core/src/content/discovery.d.ts +1 -1
  53. package/core/src/content/index.d.ts +1 -1
  54. package/core/src/content/metadata.d.ts +1 -1
  55. package/core/src/index.d.ts +3 -1
  56. package/core/src/index.d.ts.map +1 -1
  57. package/core/src/rss/discovery.d.ts +43 -0
  58. package/core/src/rss/discovery.d.ts.map +1 -0
  59. package/core/src/rss/generator.d.ts +29 -0
  60. package/core/src/rss/generator.d.ts.map +1 -0
  61. package/core/src/rss/index.d.ts +12 -0
  62. package/core/src/rss/index.d.ts.map +1 -0
  63. package/core/src/sitemap/discovery.d.ts +51 -0
  64. package/core/src/sitemap/discovery.d.ts.map +1 -0
  65. package/core/src/sitemap/generator.d.ts +42 -0
  66. package/core/src/sitemap/generator.d.ts.map +1 -0
  67. package/core/src/sitemap/index.d.ts +12 -0
  68. package/core/src/sitemap/index.d.ts.map +1 -0
  69. package/core/src/ssr/index.d.ts +1 -1
  70. package/core/src/ssr/lib/cache.d.ts +3 -2
  71. package/core/src/ssr/lib/cache.d.ts.map +1 -1
  72. package/core/src/ssr/lib/collections.d.ts +1 -1
  73. package/core/src/ssr/lib/compression.d.ts +1 -1
  74. package/core/src/ssr/lib/compute.d.ts +4 -1
  75. package/core/src/ssr/lib/compute.d.ts.map +1 -1
  76. package/core/src/ssr/lib/data.d.ts +1 -1
  77. package/core/src/ssr/lib/data.d.ts.map +1 -1
  78. package/core/src/ssr/lib/html.d.ts +1 -1
  79. package/core/src/ssr/lib/index.d.ts +1 -1
  80. package/core/src/ssr/lib/metadata.d.ts +1 -1
  81. package/core/src/ssr/lib/mime.d.ts +1 -1
  82. package/core/src/ssr/lib/modules.d.ts +1 -1
  83. package/core/src/ssr/lib/path.d.ts +1 -1
  84. package/core/src/ssr/lib/preload.d.ts +1 -1
  85. package/core/src/ssr/lib/scripts.d.ts +5 -1
  86. package/core/src/ssr/lib/scripts.d.ts.map +1 -1
  87. package/core/src/ssr/lib/seed.d.ts +1 -1
  88. package/core/src/ssr/lib/styles.d.ts +1 -1
  89. package/core/src/ssr/lib/template.d.ts +1 -1
  90. package/core/src/ssr/lib/types.d.ts +1 -1
  91. package/core/src/ssr/render.d.ts +1 -1
  92. package/core/src/ssr/render.d.ts.map +1 -1
  93. package/core/src/ssr/stream.d.ts +1 -1
  94. package/core/src/ssr/stream.d.ts.map +1 -1
  95. package/elysia/index.d.ts +1 -1
  96. package/elysia/index.js +1265 -59
  97. package/elysia/index.js.map +20 -12
  98. package/elysia/src/index.d.ts +1 -1
  99. package/elysia/src/libs/cache.d.ts +1 -1
  100. package/elysia/src/libs/http.d.ts +1 -1
  101. package/elysia/src/libs/image.d.ts +1 -1
  102. package/elysia/src/plugin.d.ts +1 -1
  103. package/elysia/src/plugin.d.ts.map +1 -1
  104. package/elysia/src/routes/artifacts.d.ts +1 -1
  105. package/elysia/src/routes/content.d.ts +1 -1
  106. package/elysia/src/routes/image.d.ts +1 -1
  107. package/elysia/src/routes/internal.d.ts +1 -1
  108. package/elysia/src/routes/redirects.d.ts +1 -1
  109. package/elysia/src/routes/rss.d.ts +27 -0
  110. package/elysia/src/routes/rss.d.ts.map +1 -0
  111. package/elysia/src/routes/sitemap.d.ts +27 -0
  112. package/elysia/src/routes/sitemap.d.ts.map +1 -0
  113. package/elysia/src/routes/ssr.d.ts +1 -1
  114. package/elysia/src/routes/static.d.ts +1 -1
  115. package/elysia/src/types.d.ts +6 -1
  116. package/elysia/src/types.d.ts.map +1 -1
  117. package/package.json +1 -1
  118. package/react/index.d.ts +1 -1
  119. package/react/index.js +78 -42
  120. package/react/index.js.map +8 -7
  121. package/react/src/components/ClientOnly.d.ts +22 -3
  122. package/react/src/components/ClientOnly.d.ts.map +1 -1
  123. package/react/src/components/ContentRoute.d.ts +1 -1
  124. package/react/src/components/Image.d.ts +1 -1
  125. package/react/src/components/LazyRoute.d.ts +1 -1
  126. package/react/src/components/Link.d.ts +1 -1
  127. package/react/src/components/Link.d.ts.map +1 -1
  128. package/react/src/components/Markdown.d.ts +1 -1
  129. package/react/src/components/Outlet.d.ts +1 -1
  130. package/react/src/components/index.d.ts +1 -1
  131. package/react/src/hooks/index.d.ts +2 -1
  132. package/react/src/hooks/index.d.ts.map +1 -1
  133. package/react/src/hooks/useContent.d.ts +10 -10
  134. package/react/src/hooks/useContent.d.ts.map +1 -1
  135. package/react/src/hooks/useData.d.ts +1 -1
  136. package/react/src/hooks/useFeed.d.ts +49 -0
  137. package/react/src/hooks/useFeed.d.ts.map +1 -0
  138. package/react/src/hooks/useLayoutData.d.ts +1 -1
  139. package/react/src/hooks/useNavigate.d.ts +1 -1
  140. package/react/src/hooks/useParams.d.ts +1 -1
  141. package/react/src/hooks/useRouter.d.ts +1 -1
  142. package/react/src/hooks/useSearchParams.d.ts +1 -1
  143. package/react/src/index.d.ts +1 -1
  144. package/react/src/lib/content.d.ts +1 -1
  145. package/react/src/lib/head.d.ts +1 -1
  146. package/react/src/lib/index.d.ts +1 -1
  147. package/react/src/lib/lazy-route.d.ts +1 -1
  148. package/react/src/lib/route-loader.d.ts +1 -1
  149. package/react/src/providers/ContentProvider.d.ts +1 -1
  150. package/react/src/providers/RerouteProvider.d.ts +1 -1
  151. package/react/src/providers/RouterProvider.d.ts +1 -1
  152. package/react/src/providers/RouterProvider.d.ts.map +1 -1
  153. package/react/src/providers/index.d.ts +1 -1
  154. package/react/src/types/any.d.ts +1 -1
  155. package/react/src/types/index.d.ts +1 -1
  156. package/react/src/types/router.d.ts +1 -1
  157. package/_/redirects/README.md +0 -63
package/README.md CHANGED
@@ -45,15 +45,17 @@ Reroute is a dead-simple file-based routing framework for building full-stack Re
45
45
  - `useRouter()` - Access router state and utilities
46
46
  - 📚 **Content Hooks**:
47
47
  - `useContent()` - Load and manage content collections with sorting
48
+ - `useFeed()` - Discover RSS feed URLs for current route
48
49
  - 🧩 **Components**:
49
50
  - `<Link>` - Client-side navigation with automatic prefetching
50
51
  - `<Image>` - Optimized images with automatic format negotiation and lazy loading
51
52
  - `<Outlet>` - Render nested route components
52
53
  - `<ContentRoute>` - Dynamic content rendering with metadata injection
54
+ - `<ClientOnly>` - Client-side only rendering with optional layout preservation (className/style props)
53
55
  - 🎭 **Providers**:
54
- - `<RouterProvider>` - Router context for the app
55
- - `<ContentProvider>` - Content system context
56
- - `<RerouteProvider>` - All-in-one provider wrapper
56
+ - `<RerouteProvider>` - **USE THIS** - All-in-one provider with routing + content + artifacts
57
+ - `<RouterProvider>` - Low-level router only (internal use, prefer RerouteProvider)
58
+ - `<ContentProvider>` - Content system context (internal use)
57
59
  - 🗂️ **SSR Data**:
58
60
  - `useData()` - Read route-level SSR data without loaders
59
61
  - `export const ssr = { data() {} }` - Route data function executed on server
@@ -116,6 +118,23 @@ Reroute is a dead-simple file-based routing framework for building full-stack Re
116
118
  - 🌍 **Language Attribute** - Dynamic `lang` attribute support
117
119
  - 🎯 **SEO Optimization** - Title, description, and custom meta tags
118
120
 
121
+ ### 🗺️ Sitemap Generation
122
+ - 🔍 **Auto-Discovery** - Automatically discovers static routes, content collections, and ssr.data routes
123
+ - 📄 **XML Generation** - Standards-compliant sitemap.xml following Google's specifications
124
+ - 💾 **Smart Caching** - Generated on first request and cached according to maxAge
125
+ - 📊 **Auto-Pagination** - Automatically splits into multiple files for sites with 50k+ URLs
126
+ - 🎨 **Custom Extractors** - Content-agnostic URL and date extraction via configuration
127
+ - 📅 **Metadata Support** - Includes lastmod, changefreq, and priority for each URL
128
+
129
+ ### 📰 RSS Feed Generation
130
+ - 🔍 **Auto-Discovery** - Discovers content collections, route ssr.data, and layout ssr.data
131
+ - 📄 **RSS & Atom Support** - Standards-compliant RSS 2.0 and Atom 1.0 formats
132
+ - 📚 **Per-Collection Feeds** - Each collection gets its own feed (e.g., /blog/feed.xml)
133
+ - 💾 **Smart Caching** - Generated on first request and cached according to maxAge
134
+ - 🎨 **Custom Extractors** - Content-agnostic extraction for title, description, author, pubDate
135
+ - 🔄 **Auto-Sorting** - Items sorted by publication date (newest first)
136
+ - 📏 **Limit Control** - Configurable maximum items per feed (default: 50)
137
+
119
138
  ### 📦 Configuration Options
120
139
  - 🎯 **Custom Assets Directory** - Configure where your client files live
121
140
  - 🔧 **URL Prefix** - Deploy to subdirectories with custom prefixes
@@ -152,10 +171,19 @@ import { useData, useParams } from 'reroute-js/react';
152
171
  import { getProduct } from '../lib/api';
153
172
 
154
173
  export const ssr = {
155
- async data({ params }: { params: { id?: string } }) {
174
+ async data({ params, set }: { params: { id?: string }; set: { status: number } }) {
156
175
  const id = Number.parseInt(params.id || '');
157
- if (!Number.isFinite(id)) return { product: null };
176
+ if (!Number.isFinite(id)) {
177
+ set.status = 404; // Set HTTP status code
178
+ return { product: null };
179
+ }
180
+
158
181
  const product = await getProduct(id);
182
+ if (!product) {
183
+ set.status = 404;
184
+ return { product: null };
185
+ }
186
+
159
187
  return { product };
160
188
  },
161
189
  };
@@ -171,6 +199,47 @@ Notes:
171
199
  - Works with Bun + Elysia, no client loaders needed for initial render
172
200
  - Data is injected as `window.__REROUTE_DATA__` and read during hydration
173
201
  - Use with existing content features (useContent); both can be seeded in the same page
202
+ - **HTTP Status Control:** Use `set.status` to set response status codes (follows Elysia's API). Non-200 responses are not cached
203
+
204
+ ## 🎭 Client-Only Rendering
205
+
206
+ The `ClientOnly` component prevents hydration mismatches for browser-specific code and supports layout preservation to prevent flickering:
207
+
208
+ ```tsx
209
+ import { ClientOnly } from 'reroute-js/react';
210
+
211
+ // Basic usage - prevent hydration mismatch
212
+ <ClientOnly fallback={<div>Loading...</div>}>
213
+ <CountdownTimer />
214
+ </ClientOnly>
215
+
216
+ // Prevent layout shift with height preservation
217
+ <ClientOnly
218
+ fallback={<PricingSkeleton />}
219
+ className="min-h-screen"
220
+ >
221
+ <PricingSection />
222
+ </ClientOnly>
223
+
224
+ // With inline styles
225
+ <ClientOnly
226
+ fallback={<Loader />}
227
+ style={{ minHeight: '600px' }}
228
+ >
229
+ <DashboardContent />
230
+ </ClientOnly>
231
+ ```
232
+
233
+ **When to use:**
234
+ - Components using browser APIs (`window`, `localStorage`, `navigator`)
235
+ - Dynamic values (`Date.now()`, `Math.random()`)
236
+ - Time-sensitive data that differs between server and client
237
+ - Large lazy-loaded components (use `className`/`style` to preserve height)
238
+
239
+ **Props:**
240
+ - `fallback` - Content to show during SSR and before hydration
241
+ - `className` - Optional className for wrapper div (useful for layout preservation)
242
+ - `style` - Optional inline styles for wrapper div (useful for layout preservation)
174
243
 
175
244
  ## 🔐 Environment Variables
176
245
 
@@ -391,6 +460,210 @@ export default function MyPage() {
391
460
  - **Optional**: Only bundle dependencies you actually use
392
461
 
393
462
 
463
+ ## 🗺️ Sitemap & RSS Feed Generation
464
+
465
+ Reroute provides automatic sitemap and RSS feed generation with zero configuration required.
466
+
467
+ ### Sitemap Generation
468
+
469
+ Generate standards-compliant XML sitemaps for search engines.
470
+
471
+ #### Quick Start
472
+
473
+ ```typescript
474
+ // reroute.config.ts
475
+ import { defineConfig } from 'reroute-js/core';
476
+
477
+ export default defineConfig({
478
+ sitemap: {
479
+ enabled: true, // opt-in (default: false)
480
+ baseUrl: 'https://your-domain.com',
481
+ },
482
+ });
483
+ ```
484
+
485
+ Your sitemap is now available at `/sitemap.xml`!
486
+
487
+ #### What Gets Included
488
+
489
+ All three sources are automatically discovered:
490
+
491
+ 1. **Static Routes** - Pages in `routes/` folder (e.g., `/`, `/about`, `/contact`)
492
+ 2. **Content Collections** - Items in `routes/[collection]/content/` with metadata
493
+ 3. **Route SSR Data** - Dynamic data from route `ssr.data` exports
494
+
495
+
496
+
497
+ #### Custom Configuration
498
+
499
+ For non-standard data structures:
500
+
501
+ ```typescript
502
+ sitemap: {
503
+ enabled: true,
504
+ baseUrl: 'https://your-domain.com',
505
+
506
+ // Extract URL segment from ssr.data items
507
+ extractUrl: (item, routePattern) => {
508
+ // Products use SKU instead of slug
509
+ if (routePattern === '/products' && item.sku) {
510
+ return item.sku; // /products/laptop-pro-2024
511
+ }
512
+ return null; // fall back to defaults (slug, id, name, key)
513
+ },
514
+
515
+ // Extract lastmod date
516
+ extractLastmod: (item) => item.updatedAt || item.date,
517
+
518
+ // Default settings
519
+ changefreq: 'weekly', // or 'daily', 'monthly', etc.
520
+ priority: 0.5, // 0.0 to 1.0
521
+ }
522
+ ```
523
+
524
+ #### Features
525
+
526
+ - **Runtime generation** - Generated on first request, cached according to `maxAge`
527
+ - **Auto-pagination** - Sites with 50k+ URLs automatically split into multiple files with sitemap index
528
+ - **SEO-friendly** - Includes `<lastmod>`, `<changefreq>`, and `<priority>` tags
529
+ - **Content-agnostic** - Works with any data structure via custom extractors
530
+ - **Smart caching** - Respects your `maxAge` configuration
531
+
532
+ ### RSS Feed Generation
533
+
534
+ Generate RSS 2.0 and Atom 1.0 feeds for content syndication.
535
+
536
+ #### Quick Start
537
+
538
+ ```typescript
539
+ // reroute.config.ts
540
+ import { defineConfig } from 'reroute-js/core';
541
+
542
+ export default defineConfig({
543
+ rss: {
544
+ enabled: true, // opt-in (default: false)
545
+ baseUrl: 'https://your-domain.com',
546
+ title: 'My Blog',
547
+ description: 'Latest posts and updates',
548
+ },
549
+ });
550
+ ```
551
+
552
+ Your feeds are automatically available at:
553
+ - `/feed.xml` - Main feed (combines all sources below)
554
+ - `/blog/feed.xml` - Blog collection (from `routes/blog/content/`)
555
+ - `/changelog/feed.xml` - Changelog route (from route's `ssr.data`)
556
+ - `/announcements/feed.xml` - Announcements (from layout's `ssr.data`)
557
+
558
+ Pattern:
559
+ - `/[collection]/feed.xml` - For any content collection
560
+ - `/[route]/feed.xml` - For any route/layout with `ssr.data`
561
+
562
+ #### What Gets Included
563
+
564
+ All three sources are automatically discovered:
565
+
566
+ 1. **Content Collections** - Items from `routes/[collection]/content/` folders
567
+ - Example: Blog posts with metadata (title, description, date)
568
+
569
+ 2. **Route SSR Data** - Arrays returned by route `ssr.data` functions
570
+ - Example: Changelog versions fetched from API
571
+ - Must export `ssr.data` that returns object containing arrays
572
+
573
+ 3. **Layout SSR Data** - Arrays returned by layout `ssr.data` functions
574
+ - Example: Site-wide announcements fetched in layout
575
+ - Must export `ssr.data` in `[layout].tsx` that returns object containing arrays
576
+
577
+ **Important:** For ssr.data discovery, the data must contain arrays. Nested arrays (e.g., `{ user: { notifications: [...] } }`) are automatically found via recursive discovery.
578
+
579
+ #### Custom Configuration
580
+
581
+ ```typescript
582
+ rss: {
583
+ enabled: true,
584
+ baseUrl: 'https://your-domain.com',
585
+ title: 'My Blog',
586
+ description: 'Latest updates',
587
+ limit: 50, // max items per feed (default: 50)
588
+ format: 'rss', // 'rss' (RSS 2.0) or 'atom' (Atom 1.0)
589
+
590
+ // Extract URL segment from ssr.data items
591
+ extractUrl: (item, routePattern) => {
592
+ // Changelog uses version field
593
+ if (routePattern === '/changelog' && item.version) {
594
+ return item.version; // /changelog/1.2.0
595
+ }
596
+ return null; // fall back to defaults (slug, id, name, key)
597
+ },
598
+
599
+ // Extract publication date
600
+ extractPubDate: (item) => item.publishedAt || item.date || item.createdAt,
601
+
602
+ // Extract author
603
+ extractAuthor: (item) => item.author || item.authorName,
604
+
605
+ // Extract full content (optional, for full-text feeds)
606
+ extractContent: (item) => item.body || item.content,
607
+ }
608
+ ```
609
+
610
+ #### Features
611
+
612
+ - **Multi-source** - Combines content collections, route ssr.data, and layout ssr.data
613
+ - **Per-collection feeds** - Each collection gets `/[collection]/feed.xml`
614
+ - **Per-route feeds** - Routes with ssr.data get `/[route]/feed.xml`
615
+ - **RSS & Atom** - Choose RSS 2.0 or Atom 1.0 format
616
+ - **Auto-sorting** - Items sorted by pubDate (newest first)
617
+ - **Smart caching** - Generated on first request, cached according to `maxAge`
618
+ - **Recursive discovery** - Finds arrays even in nested data (e.g., `user.notifications`)
619
+ - **Content-agnostic** - Works with any data structure via custom extractors
620
+
621
+ #### Discovering Feeds in Your UI
622
+
623
+ Use the `useFeed()` hook to automatically discover and link to RSS feeds:
624
+
625
+ ```tsx
626
+ import { useFeed } from 'reroute-js/react';
627
+
628
+ function Header() {
629
+ const feed = useFeed();
630
+
631
+ return (
632
+ <header>
633
+ {/* Show collection/route-specific feed if available */}
634
+ {feed.hasFeed && (
635
+ <a href={feed.feedUrl!} target="_blank">
636
+ Subscribe to {feed.collection}
637
+ </a>
638
+ )}
639
+
640
+ {/* Main feed always available */}
641
+ <a href={feed.mainFeedUrl}>RSS Feed</a>
642
+ </header>
643
+ );
644
+ }
645
+ ```
646
+
647
+ Returns:
648
+ - `feedUrl` - URL of the specific feed (`/blog/feed.xml`, `/changelog/feed.xml`, etc.) or `null`
649
+ - `mainFeedUrl` - Main feed URL (always `/feed.xml`)
650
+ - `collection` - Collection/route name (`'blog'`, `'changelog'`, etc.) or `null`
651
+ - `hasFeed` - Boolean indicating if current route has a specific feed
652
+
653
+ #### SPA Navigation Support
654
+
655
+ **Important:** When using layout `ssr.data`, use `<Link>` components (not `<a>` tags) for proper client-side navigation:
656
+
657
+ ```tsx
658
+ import { Link } from 'reroute-js/react';
659
+
660
+ // ✅ Good - prefetches layout data on hover
661
+ <Link to="/announcements">Announcements</Link>
662
+
663
+ // ❌ Bad - full page reload, no prefetch
664
+ <a href="/announcements">Announcements</a>
665
+ ```
666
+
394
667
  ## 📖 Learn More
395
668
 
396
669
  - [Examples](./examples) - Example projects to get started
@@ -3,12 +3,13 @@
3
3
  "description": "A Reroute application",
4
4
  "version": "0.0.0",
5
5
  "scripts": {
6
- "types": "tsc --noEmit",
7
- "gen": "reroute gen",
8
- "start": "reroute start",
9
- "dev": "reroute dev",
10
- "build": "reroute build",
11
- "analyze": "reroute analyze --prod --open"
6
+ "types": "tsc --noEmit",
7
+ "gen": "reroute gen",
8
+ "start": "reroute start --prod",
9
+ "dev": "reroute dev",
10
+ "build": "reroute build",
11
+ "kill": "bunx kill-port 3000",
12
+ "analyze": "reroute analyze --prod --open"
12
13
  },
13
14
  "private": true,
14
15
  "dependencies": {
@@ -3,10 +3,5 @@ import { defineConfig } from 'reroute-js/core';
3
3
  export default defineConfig({
4
4
  options: {
5
5
  app: './src/client/App',
6
- maxAge: 3600,
7
- compression: true
8
- },
9
- streaming: {
10
- enabled: true,
11
6
  },
12
7
  });
@@ -5,7 +5,10 @@ const app = new Elysia()
5
5
  .use(
6
6
  reroute(),
7
7
  )
8
- .get('/api/message', () => ({ message: 'Hello from server' }))
8
+ .get('/api/message', () => {
9
+ console.log('[api] log from my api')
10
+ return { message: 'Hello from server' }
11
+ })
9
12
  .listen(Number(Bun.env.PORT || '3000'));
10
13
 
11
- console.log(`[reroute] Running at ${app.server?.hostname}:${app.server?.port}`);
14
+ console.log(`[app] Running at ${app.server?.hostname}:${app.server?.port}`);
@@ -3,12 +3,13 @@
3
3
  "description": "A Reroute application",
4
4
  "version": "0.0.0",
5
5
  "scripts": {
6
- "types": "tsc --noEmit",
7
- "gen": "reroute gen",
8
- "start": "reroute start",
9
- "dev": "reroute dev",
10
- "build": "reroute build",
11
- "analyze": "reroute analyze --prod --open"
6
+ "types": "tsc --noEmit",
7
+ "gen": "reroute gen",
8
+ "start": "PORT=3001 reroute start --prod",
9
+ "dev": "PORT=3001 reroute dev",
10
+ "build": "reroute build",
11
+ "kill": "bunx kill-port 3001",
12
+ "analyze": "reroute analyze --prod --open"
12
13
  },
13
14
  "private": true,
14
15
  "dependencies": {
@@ -9,8 +9,8 @@ const app = new Elysia()
9
9
  console.log('[api] log from my api');
10
10
  return { message: 'Hello from server' };
11
11
  })
12
- .listen(Number(Bun.env.PORT || '3000'));
12
+ .listen(Number(Bun.env.PORT || '3001'));
13
13
 
14
14
  console.log(
15
- `[reroute] Blog running at ${app.server?.hostname}:${app.server?.port} on ${Bun.env.NODE_ENV || 'development'}`,
15
+ `[app] Blog running at ${app.server?.hostname}:${app.server?.port} on ${Bun.env.NODE_ENV || 'development'}`,
16
16
  );
@@ -4,12 +4,13 @@
4
4
  "description": "A Reroute application",
5
5
  "private": true,
6
6
  "scripts": {
7
- "types": "tsc --noEmit",
8
- "gen": "reroute gen",
9
- "start": "reroute start",
10
- "dev": "reroute dev",
11
- "build": "reroute build",
12
- "analyze": "reroute analyze --prod --open"
7
+ "types": "tsc --noEmit",
8
+ "gen": "reroute gen",
9
+ "start": "PORT=3002 reroute start --prod",
10
+ "dev": "PORT=3002 reroute dev",
11
+ "build": "reroute build",
12
+ "kill": "bunx kill-port 3002",
13
+ "analyze": "reroute analyze --prod --open"
13
14
  },
14
15
  "dependencies": {
15
16
  "reroute-js": "latest",
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "{{PROJECT_NAME}}",
3
+ "version": "0.0.0",
4
+ "description": "A Reroute application",
5
+ "private": true,
6
+ "scripts": {
7
+ "types": "tsc --noEmit",
8
+ "gen": "reroute gen",
9
+ "start": "PORT=3003 reroute start --prod",
10
+ "dev": "PORT=3003 reroute dev",
11
+ "build": "reroute build",
12
+ "kill": "bunx kill-port 3003",
13
+ "analyze": "reroute analyze --prod --open"
14
+ },
15
+ "dependencies": {
16
+ "reroute-js": "latest",
17
+ "elysia": "^1.4.12",
18
+ "react": "^19.2.0",
19
+ "react-dom": "^19.2.0",
20
+ "sharp": "^0.34.4"
21
+ },
22
+ "devDependencies": {
23
+ "@types/bun": "latest",
24
+ "@types/react": "^19.2.2",
25
+ "@types/react-dom": "^19.2.2",
26
+ "typescript": "^5"
27
+ }
28
+ }
@@ -0,0 +1,23 @@
1
+ import { defineConfig } from 'reroute-js/core';
2
+
3
+ export default defineConfig({
4
+ options: {
5
+ app: './src/client/App',
6
+ maxAge: 3600,
7
+ compression: true,
8
+ },
9
+ sitemap: {
10
+ enabled: true,
11
+ baseUrl: 'http://localhost:3003',
12
+ // Test pagination with small page size (default 50k)
13
+ // maxUrlsPerPage: 5,
14
+ // Custom URL extractor - products use SKU instead of default fields
15
+ extractUrl: (item, routePattern) => {
16
+ if (routePattern === '/products' && item.sku) {
17
+ return item.sku;
18
+ }
19
+ return null; // fall back to defaults (slug, id, name, etc.)
20
+ },
21
+ extractLastmod: (item) => item.updatedAt || item.date,
22
+ },
23
+ });
@@ -0,0 +1,25 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "useDefineForClassFields": true,
5
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
6
+ "module": "ESNext",
7
+ "skipLibCheck": true,
8
+ "moduleResolution": "bundler",
9
+ "isolatedModules": true,
10
+ "baseUrl": ".",
11
+ "rootDir": ".",
12
+ "declaration": true,
13
+ "declarationMap": true,
14
+ "outDir": "dist/types",
15
+ "emitDeclarationOnly": true,
16
+ "strict": true,
17
+ "noUnusedLocals": true,
18
+ "noUnusedParameters": true,
19
+ "noFallthroughCasesInSwitch": true,
20
+ "jsx": "react-jsx",
21
+ "types": ["bun", "node"]
22
+ },
23
+ "include": ["**/*.ts", "**/*.tsx"],
24
+ "exclude": ["node_modules", "dist"]
25
+ }
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "{{PROJECT_NAME}}",
3
+ "version": "0.0.0",
4
+ "description": "A Reroute application",
5
+ "private": true,
6
+ "scripts": {
7
+ "types": "tsc --noEmit",
8
+ "gen": "reroute gen",
9
+ "start": "PORT=3004 reroute start --prod",
10
+ "dev": "PORT=3004 reroute dev",
11
+ "build": "reroute build",
12
+ "kill": "bunx kill-port 3004",
13
+ "analyze": "reroute analyze --prod --open"
14
+ },
15
+ "dependencies": {
16
+ "reroute-js": "latest",
17
+ "elysia": "^1.4.12",
18
+ "react": "^19.2.0",
19
+ "react-dom": "^19.2.0",
20
+ "sharp": "^0.34.4"
21
+ },
22
+ "devDependencies": {
23
+ "@types/bun": "latest",
24
+ "@types/react": "^19.2.2",
25
+ "@types/react-dom": "^19.2.2",
26
+ "typescript": "^5"
27
+ }
28
+ }
@@ -0,0 +1,23 @@
1
+ import { defineConfig } from 'reroute-js/core';
2
+
3
+ export default defineConfig({
4
+ options: {
5
+ app: './src/client/App',
6
+ maxAge: 3600,
7
+ compression: true,
8
+ },
9
+ sitemap: {
10
+ enabled: true,
11
+ baseUrl: 'http://localhost:3003',
12
+ // Test pagination with small page size (default 50k)
13
+ // maxUrlsPerPage: 5,
14
+ // Custom URL extractor - products use SKU instead of default fields
15
+ extractUrl: (item, routePattern) => {
16
+ if (routePattern === '/products' && item.sku) {
17
+ return item.sku;
18
+ }
19
+ return null; // fall back to defaults (slug, id, name, etc.)
20
+ },
21
+ extractLastmod: (item) => item.updatedAt || item.date,
22
+ },
23
+ });
@@ -0,0 +1,25 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "useDefineForClassFields": true,
5
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
6
+ "module": "ESNext",
7
+ "skipLibCheck": true,
8
+ "moduleResolution": "bundler",
9
+ "isolatedModules": true,
10
+ "baseUrl": ".",
11
+ "rootDir": ".",
12
+ "declaration": true,
13
+ "declarationMap": true,
14
+ "outDir": "dist/types",
15
+ "emitDeclarationOnly": true,
16
+ "strict": true,
17
+ "noUnusedLocals": true,
18
+ "noUnusedParameters": true,
19
+ "noFallthroughCasesInSwitch": true,
20
+ "jsx": "react-jsx",
21
+ "types": ["bun", "node"]
22
+ },
23
+ "include": ["**/*.ts", "**/*.tsx"],
24
+ "exclude": ["node_modules", "dist"]
25
+ }
@@ -4,12 +4,13 @@
4
4
  "description": "A Reroute store application with Tailwind CSS v4",
5
5
  "private": true,
6
6
  "scripts": {
7
- "types": "tsc --noEmit",
8
- "gen": "reroute gen",
9
- "start": "reroute start",
10
- "dev": "reroute dev",
11
- "build": "reroute build",
12
- "analyze": "reroute analyze --prod --open"
7
+ "types": "tsc --noEmit",
8
+ "gen": "reroute gen",
9
+ "start": "PORT=3005 reroute start --prod",
10
+ "dev": "PORT=3005 reroute dev",
11
+ "build": "reroute build",
12
+ "kill": "bunx kill-port 3005",
13
+ "analyze": "reroute analyze --prod --open"
13
14
  },
14
15
  "dependencies": {
15
16
  "reroute-js": "latest",
@@ -1,13 +1,10 @@
1
1
  import { Elysia } from 'elysia';
2
2
  import { reroute } from 'reroute-js/elysia';
3
3
 
4
-
5
4
  const app = new Elysia()
6
- .use(
7
- reroute(),
8
- )
9
- .listen(Number(Bun.env.PORT || '3001'));
5
+ .use(reroute())
6
+ .listen(Number(Bun.env.PORT || '3005'));
10
7
 
11
8
  console.log(
12
- `[reroute] Store running at ${app.server?.hostname}:${app.server?.port} on ${Bun.env.NODE_ENV || 'development'}`,
9
+ `[app] Store example running at ${app.server?.hostname}:${app.server?.port} on ${Bun.env.NODE_ENV || 'development'}`,
13
10
  );
@@ -4,12 +4,13 @@
4
4
  "description": "A Reroute application",
5
5
  "private": true,
6
6
  "scripts": {
7
- "types": "tsc --noEmit",
8
- "gen": "reroute gen",
9
- "start": "reroute start",
10
- "dev": "reroute dev",
11
- "build": "reroute build",
12
- "analyze": "reroute analyze --prod --open"
7
+ "types": "tsc --noEmit",
8
+ "gen": "reroute gen",
9
+ "start": "PORT=3006 reroute start --prod",
10
+ "dev": "PORT=3006 reroute dev",
11
+ "build": "reroute build",
12
+ "kill": "bunx kill-port 3006",
13
+ "analyze": "reroute analyze --prod --open"
13
14
  },
14
15
  "dependencies": {
15
16
  "reroute-js": "latest",
package/cli/bin.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * reroute-js v0.11.5
2
+ * reroute-js v0.13.0
3
3
  *
4
4
  * @license MIT
5
5
  * @copyright 2025 stewones <hi@stewan.io>