Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/thihathit/rutter
Type-safe framework-agnostic Router, built with URLPattern & History API.
https://github.com/thihathit/rutter
framework-agnostic history-api react router solid svelte type-safe typescript urlpattern vue
Last synced: 3 months ago
JSON representation
Type-safe framework-agnostic Router, built with URLPattern & History API.
- Host: GitHub
- URL: https://github.com/thihathit/rutter
- Owner: thihathit
- License: mit
- Created: 2023-03-15T12:52:21.000Z (almost 2 years ago)
- Default Branch: master
- Last Pushed: 2024-03-06T01:50:13.000Z (11 months ago)
- Last Synced: 2024-08-09T11:14:52.213Z (6 months ago)
- Topics: framework-agnostic, history-api, react, router, solid, svelte, type-safe, typescript, urlpattern, vue
- Language: TypeScript
- Homepage: https://npmjs.com/package/rutter
- Size: 182 KB
- Stars: 6
- Watchers: 1
- Forks: 0
- Open Issues: 3
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
## About
**Rutter** is a framework-agnostic, lightweight router. Built with [URLPattern](https://developer.mozilla.org/en-US/docs/Web/API/URLPattern) & [History](https://developer.mozilla.org/en-US/docs/Web/API/History_API) API. Internal reactivity is powered by [Signal](https://github.com/preactjs/signals).
> This library doesn't ship polyfill for `URLPattern`. You may consider installing [urlpattern-polyfill](https://www.npmjs.com/package/urlpattern-polyfill).
## Usage
- [ Vanilla JS](#-vanillajs)
- [ Vue](#-vue-bindings-via-shallowrefcomputed)
- [ React](#-react-bindings-via-usestatecontext)
- [ Svelte](#-svelte-bindings-via-readablederived)### VanillaJS
```ts
import { CreateHistory } from 'rutter'const router = new CreateHistory({
routes: {
index: {
pathname: ''
},
about: {
pathname: '/about'
},
blog: {
pathname: '/blog'
},
blogDetail: {
pathname: '/blog/:id'
}
}
})router.on('index') // boolean
router.onOneOf(['index', 'about']) // boolean
```### React bindings: via `useState`/`context`
```tsx
// router.(tsx|jsx)import {
FC,
PropsWithChildren,
createContext,
useContext,
useEffect,
useState
} from 'react'import { CreateHistory } from 'rutter'
export const {
redirect,
on,
summaryState,
routeState,
watchSummaryState,
watchRouteState
} = new CreateHistory({
routes: {
index: {
pathname: ''
},
about: {
pathname: '/about'
},
blog: {
pathname: '/blog'
},
blogDetail: {
pathname: '/blog/:id'
}
}
})/**
* Although using with `context` is recommended for performance reason, you can directly use this hook if you don't want to store all the states in `context` tree.
*/
export const useRouterValues = () => {
const [routeStateValue, setRouteStateState] = useState(routeState)
const [summaryStateValue, setSummaryStateState] = useState(summaryState)useEffect(() => watchRouteState(setRouteStateState), [])
useEffect(() => watchSummaryState(setSummaryStateState), [])return {
routeState: routeStateValue,
summaryState: summaryStateValue
}
}const context = createContext({
routeState,
summaryState
})const useRouterContext = () => useContext(context)
export const RouterProvider: FC = ({ children }) => {
const value = useRouterValues()return {children}
}export const useRoute = () => {
const { routeState } = useRouterContext()return routeState
}
``````tsx
// app.(tsx|jsx)import { FC } from 'react'
import { on, redirect, useRoute, RouterProvider } from './router'
const Routing: FC = () => {
const { is404, ...restStates } = useRoute()return (
<>
redirect('index')}>Indexredirect('blog')}>Blog
Body:
{is404 ? (
404 Page
) : (
<>
{on('index') &&Index Page
}{on('about') &&
About Page
}{on('blog') && (
<>
Blog Page
redirect('blogDetail', {
params: {
id: 123
}
})
}
>
Blog Detail
>
)}{on('blogDetail') &&
Blog Detail Page
}
>
)}
Current route detail:
{JSON.stringify(restStates, null, 2)}
>
)
}const App: FC = () => (
)
```### Vue bindings: via `shallowRef`/`computed`
```ts
// router.(ts|js)import { computed, shallowRef } from 'vue'
import { CreateHistory } from 'rutter'import { mapValues } from 'lodash-es'
const router = new CreateHistory({
routes: {
index: {
pathname: ''
},
about: {
pathname: '/about'
},
blog: {
pathname: '/blog'
},
blogDetail: {
pathname: '/blog/:id'
}
}
})const {
//
summaryState,
routeState,
watchSummaryState,
watchRouteState,
on
} = routerexport const { redirect } = router
export const routerState = shallowRef(summaryState)
export const route = shallowRef(routeState)export const is404 = computed(() => route.value.is404)
export const matches = computed(() => {
const { details } = routerState.valuetype RouteNames = keyof typeof details
return mapValues(details, (_, name) => on(name as RouteNames))
})watchSummaryState(state => {
routerState.value = state
})watchRouteState(state => {
route.value = state
})
``````vue
// app.vue
import { redirect, route, matches, is404 } from './router'
redirect('index')">Indexredirect('blog')">Blog
Body:
404 Page
Index Page
About Page
Blog Page
redirect('blogDetail', { params: { id: 123 } })"
>
Blog Detail
Blog Detail Page
Current route detail:
{{ route }}
```
### Svelte bindings: via `readable`/`derived`
```ts
// router.(ts|js)import { readable, derived } from 'svelte/store'
import { CreateHistory } from 'rutter'import { mapValues } from 'lodash-es'
const router = new CreateHistory({
routes: {
index: {
pathname: ''
},
about: {
pathname: '/about'
},
blog: {
pathname: '/blog'
},
blogDetail: {
pathname: '/blog/:id'
}
}
})const { summaryState, routeState, watchSummaryState, watchRouteState } = router
export const { redirect, on, onOneOf } = router
export const route = readable(routeState, watchRouteState)
export const routerState = readable(summaryState, watchSummaryState)export const matches = derived(routerState, ({ details }) =>
mapValues(details, (_, name) => on(name as keyof typeof details))
)
``````svelte
// app.svelte
import { redirect, route, matches } from './router'
$: ({ is404, ...restState } = $route)
$: data = JSON.stringify(restState, null, 2)redirect('index')}>Index
redirect('blog')}>Blog
Body:
{#if is404}
404 Page
{:else}
{#if $matches.index}
Index Page
{/if}{#if $matches.about}
About Page
{/if}{#if $matches.blog}
Blog Page
redirect('blogDetail', { params: { id: 123 } })}
>
Blog Detail
{/if}{#if $matches.blogDetail}
Blog Detail Page
{/if}
{/if}
Current route detail:
{data}
```
## Documentation
Type API: https://paka.dev/npm/rutter/api
## Development
```bash
pnpm i
pnpm dev
```