@pyreon/router 0.24.5 → 0.24.6
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/package.json +4 -6
- package/src/components.tsx +0 -650
- package/src/env.d.ts +0 -6
- package/src/index.ts +0 -106
- package/src/loader.ts +0 -200
- package/src/manifest.ts +0 -399
- package/src/match.ts +0 -921
- package/src/not-found.ts +0 -75
- package/src/redirect.ts +0 -63
- package/src/router.ts +0 -1424
- package/src/scroll.ts +0 -93
- package/src/tests/integration.test.tsx +0 -298
- package/src/tests/loader.test.ts +0 -1024
- package/src/tests/manifest-snapshot.test.ts +0 -101
- package/src/tests/match.test.ts +0 -782
- package/src/tests/native-markers.test.ts +0 -18
- package/src/tests/redirect.test.ts +0 -96
- package/src/tests/router.browser.test.tsx +0 -509
- package/src/tests/router.test.ts +0 -5498
- package/src/tests/routerlink-reactive-to.browser.test.tsx +0 -158
- package/src/tests/scroll.test.ts +0 -31
- package/src/tests/setup.ts +0 -3
- package/src/types.ts +0 -517
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
import { _rp, h } from '@pyreon/core'
|
|
2
|
-
import { flush, mountInBrowser } from '@pyreon/test-utils/browser'
|
|
3
|
-
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
|
|
4
|
-
import { createRouter, RouterLink, RouterProvider, useIsActive } from '../index'
|
|
5
|
-
import { setActiveRouter } from '../router'
|
|
6
|
-
|
|
7
|
-
describe('RouterLink reactive `to` prop', () => {
|
|
8
|
-
beforeEach(() => {
|
|
9
|
-
window.location.hash = ''
|
|
10
|
-
})
|
|
11
|
-
afterEach(() => {
|
|
12
|
-
setActiveRouter(null)
|
|
13
|
-
window.location.hash = ''
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
it('resolves _rp-wrapped `to` accessor to its string value, not the function literal', async () => {
|
|
17
|
-
const routes = [{ path: '/', component: () => h('div', null, 'home') }]
|
|
18
|
-
const router = createRouter({ routes, mode: 'hash' })
|
|
19
|
-
const { container, unmount } = mountInBrowser(
|
|
20
|
-
h(
|
|
21
|
-
RouterProvider,
|
|
22
|
-
{ router },
|
|
23
|
-
h(RouterLink, { to: _rp(() => '/about') as unknown as string, id: 'link' }, 'Go'),
|
|
24
|
-
),
|
|
25
|
-
)
|
|
26
|
-
await flush()
|
|
27
|
-
const link = container.querySelector<HTMLAnchorElement>('#link')
|
|
28
|
-
expect(link).not.toBeNull()
|
|
29
|
-
expect(link!.getAttribute('href')).toBe('#/about')
|
|
30
|
-
unmount()
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
it('NavItem-shape: parent gets `path` as `_rp` getter and forwards it to RouterLink', async () => {
|
|
34
|
-
// Mirrors `examples/fundamentals-playground/src/routes/_layout.tsx`:
|
|
35
|
-
// function NavItem(props) {
|
|
36
|
-
// return <RouterLink to={props.path} ...>{props.label}</RouterLink>
|
|
37
|
-
// }
|
|
38
|
-
// <NavItem path={tab.path} ... />
|
|
39
|
-
// The compiler emits `_rp(() => tab.path)` for the parent's `path` prop,
|
|
40
|
-
// which `makeReactiveProps` turns into a getter on NavItem's `props`.
|
|
41
|
-
// Then `<RouterLink to={props.path}>` re-wraps that getter access as
|
|
42
|
-
// `_rp(() => props.path)`. The outermost getter points back through
|
|
43
|
-
// NavItem's getter to the literal value.
|
|
44
|
-
const NavItem = (props: Record<string, unknown>) =>
|
|
45
|
-
h(RouterLink, {
|
|
46
|
-
to: _rp(() => props.path as string) as unknown as string,
|
|
47
|
-
id: 'nav-link',
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
const routes = [
|
|
51
|
-
{ path: '/', component: () => h('div', { id: 'home' }, 'home') },
|
|
52
|
-
{ path: '/about', component: () => h('div', { id: 'about' }, 'about') },
|
|
53
|
-
]
|
|
54
|
-
const router = createRouter({ routes, mode: 'hash' })
|
|
55
|
-
const { container, unmount } = mountInBrowser(
|
|
56
|
-
h(
|
|
57
|
-
RouterProvider,
|
|
58
|
-
{ router },
|
|
59
|
-
h(NavItem, { path: _rp(() => '/about') as unknown as string }),
|
|
60
|
-
),
|
|
61
|
-
)
|
|
62
|
-
await flush()
|
|
63
|
-
const link = container.querySelector<HTMLAnchorElement>('#nav-link')
|
|
64
|
-
expect(link).not.toBeNull()
|
|
65
|
-
expect(link!.getAttribute('href')).toBe('#/about')
|
|
66
|
-
expect(link!.getAttribute('href')).not.toContain('=>')
|
|
67
|
-
unmount()
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
it('activeClass updates reactively when `to` is `_rp`-wrapped and route changes', async () => {
|
|
71
|
-
// Pre-fix, `RouterLink`'s own `activeClass` computation read `props.to`
|
|
72
|
-
// ONCE at component setup time and compared against the current path.
|
|
73
|
-
// Even if `props.to` correctly resolved to the string, the comparison
|
|
74
|
-
// was static — `activeClass` was a function returning a string but the
|
|
75
|
-
// captured `target = props.to` (declared inline) was hoisted in setup
|
|
76
|
-
// scope. After fixing setup-time captures of `props.to` (so the activeClass
|
|
77
|
-
// accessor reads `props.to` lazily on each invocation), navigation
|
|
78
|
-
// updates the class reactively.
|
|
79
|
-
const routes = [
|
|
80
|
-
{ path: '/', component: () => h('div', { id: 'home' }, 'home') },
|
|
81
|
-
{ path: '/about', component: () => h('div', { id: 'about' }, 'about') },
|
|
82
|
-
]
|
|
83
|
-
const router = createRouter({ routes, mode: 'hash' })
|
|
84
|
-
|
|
85
|
-
const NavItem = (props: Record<string, unknown>) =>
|
|
86
|
-
h(RouterLink, {
|
|
87
|
-
to: _rp(() => props.path as string) as unknown as string,
|
|
88
|
-
id: `link-${(props as { _id: string })._id}`,
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
const { container, unmount } = mountInBrowser(
|
|
92
|
-
h(
|
|
93
|
-
RouterProvider,
|
|
94
|
-
{ router },
|
|
95
|
-
h('div', null, [
|
|
96
|
-
h(NavItem, { _id: 'home', path: _rp(() => '/') as unknown as string }),
|
|
97
|
-
h(NavItem, { _id: 'about', path: _rp(() => '/about') as unknown as string }),
|
|
98
|
-
]),
|
|
99
|
-
),
|
|
100
|
-
)
|
|
101
|
-
await flush()
|
|
102
|
-
|
|
103
|
-
// RouterLink applies the default `router-link-active` class when the
|
|
104
|
-
// current path matches `to`.
|
|
105
|
-
expect(container.querySelector('#link-home')!.className).toContain('router-link-active')
|
|
106
|
-
expect(container.querySelector('#link-about')!.className).not.toContain('router-link-active')
|
|
107
|
-
|
|
108
|
-
await router.push('/about')
|
|
109
|
-
await flush()
|
|
110
|
-
|
|
111
|
-
expect(container.querySelector('#link-about')!.className).toContain('router-link-active')
|
|
112
|
-
expect(container.querySelector('#link-home')!.className).not.toContain('router-link-active')
|
|
113
|
-
unmount()
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
it('useIsActive(props.path) reactively flips when current route matches', async () => {
|
|
117
|
-
// The full fundamentals layout shape: NavItem reads `props.path` (a
|
|
118
|
-
// getter from `_rp`) and passes it BOTH to `RouterLink.to` AND to
|
|
119
|
-
// `useIsActive`. Pre-fix, `useIsActive(props.path)` captured the
|
|
120
|
-
// getter as the `path` argument — non-string — and silently returned
|
|
121
|
-
// false for every route check.
|
|
122
|
-
const routes = [
|
|
123
|
-
{ path: '/', component: () => h('div', { id: 'home' }, 'home') },
|
|
124
|
-
{ path: '/about', component: () => h('div', { id: 'about' }, 'about') },
|
|
125
|
-
]
|
|
126
|
-
const router = createRouter({ routes, mode: 'hash' })
|
|
127
|
-
|
|
128
|
-
const NavItem = (props: Record<string, unknown>) => {
|
|
129
|
-
const isActive = useIsActive(props.path as string, true)
|
|
130
|
-
return h(RouterLink, {
|
|
131
|
-
to: _rp(() => props.path as string) as unknown as string,
|
|
132
|
-
id: `link-${(props as { _id: string })._id}`,
|
|
133
|
-
class: () => (isActive() ? 'is-active' : ''),
|
|
134
|
-
})
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const { container, unmount } = mountInBrowser(
|
|
138
|
-
h(
|
|
139
|
-
RouterProvider,
|
|
140
|
-
{ router },
|
|
141
|
-
h('div', null, [
|
|
142
|
-
h(NavItem, { _id: 'home', path: _rp(() => '/') as unknown as string }),
|
|
143
|
-
h(NavItem, { _id: 'about', path: _rp(() => '/about') as unknown as string }),
|
|
144
|
-
]),
|
|
145
|
-
),
|
|
146
|
-
)
|
|
147
|
-
await flush()
|
|
148
|
-
|
|
149
|
-
await router.push('/about')
|
|
150
|
-
await flush()
|
|
151
|
-
|
|
152
|
-
// The /about NavItem's useIsActive should now be true → className
|
|
153
|
-
// includes 'is-active'.
|
|
154
|
-
expect(container.querySelector('#link-about')!.className).toContain('is-active')
|
|
155
|
-
expect(container.querySelector('#link-home')!.className).not.toContain('is-active')
|
|
156
|
-
unmount()
|
|
157
|
-
})
|
|
158
|
-
})
|
package/src/tests/scroll.test.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from 'vitest'
|
|
2
|
-
import { ScrollManager } from '../scroll'
|
|
3
|
-
|
|
4
|
-
describe('ScrollManager — LRU bound', () => {
|
|
5
|
-
test('evicts oldest entry when cap (100) is exceeded', () => {
|
|
6
|
-
const mgr = new ScrollManager('top')
|
|
7
|
-
// Fake window.scrollY for each save — happy-dom provides window.
|
|
8
|
-
Object.defineProperty(window, 'scrollY', { value: 42, configurable: true })
|
|
9
|
-
// Save 150 distinct paths.
|
|
10
|
-
for (let i = 0; i < 150; i++) mgr.save(`/path-${i}`)
|
|
11
|
-
// Oldest 50 evicted; newest 100 remain.
|
|
12
|
-
expect(mgr.getSavedPosition('/path-0')).toBeNull()
|
|
13
|
-
expect(mgr.getSavedPosition('/path-49')).toBeNull()
|
|
14
|
-
expect(mgr.getSavedPosition('/path-50')).toBe(42)
|
|
15
|
-
expect(mgr.getSavedPosition('/path-149')).toBe(42)
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
test('re-saving an existing path bumps it to newest (LRU, not FIFO)', () => {
|
|
19
|
-
const mgr = new ScrollManager('top')
|
|
20
|
-
Object.defineProperty(window, 'scrollY', { value: 42, configurable: true })
|
|
21
|
-
for (let i = 0; i < 100; i++) mgr.save(`/path-${i}`)
|
|
22
|
-
// Touch the oldest entry — should move to newest.
|
|
23
|
-
Object.defineProperty(window, 'scrollY', { value: 99, configurable: true })
|
|
24
|
-
mgr.save('/path-0')
|
|
25
|
-
// Now add one more to push out the new-oldest (/path-1).
|
|
26
|
-
mgr.save('/new-path')
|
|
27
|
-
expect(mgr.getSavedPosition('/path-1')).toBeNull()
|
|
28
|
-
expect(mgr.getSavedPosition('/path-0')).toBe(99)
|
|
29
|
-
expect(mgr.getSavedPosition('/new-path')).toBe(99)
|
|
30
|
-
})
|
|
31
|
-
})
|
package/src/tests/setup.ts
DELETED