@tanstack/solid-router 1.167.5 → 1.168.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.
- package/dist/cjs/Match.cjs +244 -223
- package/dist/cjs/Match.cjs.map +1 -1
- package/dist/cjs/Match.d.cts +1 -3
- package/dist/cjs/Matches.cjs +32 -31
- package/dist/cjs/Matches.cjs.map +1 -1
- package/dist/cjs/Scripts.cjs +10 -8
- package/dist/cjs/Scripts.cjs.map +1 -1
- package/dist/cjs/Scripts.d.cts +2 -1
- package/dist/cjs/Transitioner.cjs +26 -26
- package/dist/cjs/Transitioner.cjs.map +1 -1
- package/dist/cjs/headContentUtils.cjs +15 -15
- package/dist/cjs/headContentUtils.cjs.map +1 -1
- package/dist/cjs/index.cjs +1 -1
- package/dist/cjs/index.dev.cjs +1 -1
- package/dist/cjs/link.cjs +119 -84
- package/dist/cjs/link.cjs.map +1 -1
- package/dist/cjs/matchContext.cjs +7 -5
- package/dist/cjs/matchContext.cjs.map +1 -1
- package/dist/cjs/matchContext.d.cts +8 -2
- package/dist/cjs/not-found.cjs +8 -4
- package/dist/cjs/not-found.cjs.map +1 -1
- package/dist/cjs/not-found.d.cts +1 -1
- package/dist/cjs/router.cjs +2 -1
- package/dist/cjs/router.cjs.map +1 -1
- package/dist/cjs/routerStores.cjs +67 -0
- package/dist/cjs/routerStores.cjs.map +1 -0
- package/dist/cjs/routerStores.d.cts +10 -0
- package/dist/cjs/ssr/RouterClient.cjs +1 -1
- package/dist/cjs/ssr/RouterClient.cjs.map +1 -1
- package/dist/cjs/ssr/renderRouterToStream.cjs +1 -1
- package/dist/cjs/ssr/renderRouterToStream.cjs.map +1 -1
- package/dist/cjs/ssr/renderRouterToString.cjs +2 -2
- package/dist/cjs/ssr/renderRouterToString.cjs.map +1 -1
- package/dist/cjs/ssr/renderRouterToString.d.cts +1 -1
- package/dist/cjs/useCanGoBack.cjs +6 -2
- package/dist/cjs/useCanGoBack.cjs.map +1 -1
- package/dist/cjs/useCanGoBack.d.cts +2 -1
- package/dist/cjs/useLoaderDeps.cjs +2 -3
- package/dist/cjs/useLoaderDeps.cjs.map +1 -1
- package/dist/cjs/useLocation.cjs +13 -2
- package/dist/cjs/useLocation.cjs.map +1 -1
- package/dist/cjs/useMatch.cjs +17 -15
- package/dist/cjs/useMatch.cjs.map +1 -1
- package/dist/cjs/useParams.cjs +1 -1
- package/dist/cjs/useParams.cjs.map +1 -1
- package/dist/cjs/useRouterState.cjs +12 -19
- package/dist/cjs/useRouterState.cjs.map +1 -1
- package/dist/cjs/useSearch.cjs +2 -1
- package/dist/cjs/useSearch.cjs.map +1 -1
- package/dist/cjs/utils.cjs +0 -14
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/cjs/utils.d.cts +0 -4
- package/dist/esm/Match.d.ts +1 -3
- package/dist/esm/Match.js +245 -224
- package/dist/esm/Match.js.map +1 -1
- package/dist/esm/Matches.js +34 -33
- package/dist/esm/Matches.js.map +1 -1
- package/dist/esm/Scripts.d.ts +2 -1
- package/dist/esm/Scripts.js +8 -7
- package/dist/esm/Scripts.js.map +1 -1
- package/dist/esm/Transitioner.js +26 -26
- package/dist/esm/Transitioner.js.map +1 -1
- package/dist/esm/headContentUtils.js +15 -15
- package/dist/esm/headContentUtils.js.map +1 -1
- package/dist/esm/index.dev.js +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/link.js +120 -85
- package/dist/esm/link.js.map +1 -1
- package/dist/esm/matchContext.d.ts +8 -2
- package/dist/esm/matchContext.js +7 -4
- package/dist/esm/matchContext.js.map +1 -1
- package/dist/esm/not-found.d.ts +1 -1
- package/dist/esm/not-found.js +6 -3
- package/dist/esm/not-found.js.map +1 -1
- package/dist/esm/router.js +2 -1
- package/dist/esm/router.js.map +1 -1
- package/dist/esm/routerStores.d.ts +10 -0
- package/dist/esm/routerStores.js +65 -0
- package/dist/esm/routerStores.js.map +1 -0
- package/dist/esm/ssr/RouterClient.js +1 -1
- package/dist/esm/ssr/RouterClient.js.map +1 -1
- package/dist/esm/ssr/renderRouterToStream.js +1 -1
- package/dist/esm/ssr/renderRouterToStream.js.map +1 -1
- package/dist/esm/ssr/renderRouterToString.d.ts +1 -1
- package/dist/esm/ssr/renderRouterToString.js +2 -2
- package/dist/esm/ssr/renderRouterToString.js.map +1 -1
- package/dist/esm/useCanGoBack.d.ts +2 -1
- package/dist/esm/useCanGoBack.js +4 -2
- package/dist/esm/useCanGoBack.js.map +1 -1
- package/dist/esm/useLoaderDeps.js +2 -3
- package/dist/esm/useLoaderDeps.js.map +1 -1
- package/dist/esm/useLocation.js +11 -2
- package/dist/esm/useLocation.js.map +1 -1
- package/dist/esm/useMatch.js +18 -16
- package/dist/esm/useMatch.js.map +1 -1
- package/dist/esm/useParams.js +1 -1
- package/dist/esm/useParams.js.map +1 -1
- package/dist/esm/useRouterState.js +10 -18
- package/dist/esm/useRouterState.js.map +1 -1
- package/dist/esm/useSearch.js +2 -1
- package/dist/esm/useSearch.js.map +1 -1
- package/dist/esm/utils.d.ts +0 -4
- package/dist/esm/utils.js +1 -14
- package/dist/esm/utils.js.map +1 -1
- package/dist/source/Match.d.ts +1 -3
- package/dist/source/Match.jsx +246 -237
- package/dist/source/Match.jsx.map +1 -1
- package/dist/source/Matches.jsx +42 -44
- package/dist/source/Matches.jsx.map +1 -1
- package/dist/source/Scripts.d.ts +2 -1
- package/dist/source/Scripts.jsx +31 -36
- package/dist/source/Scripts.jsx.map +1 -1
- package/dist/source/Transitioner.jsx +26 -31
- package/dist/source/Transitioner.jsx.map +1 -1
- package/dist/source/headContentUtils.jsx +64 -72
- package/dist/source/headContentUtils.jsx.map +1 -1
- package/dist/source/link.jsx +136 -107
- package/dist/source/link.jsx.map +1 -1
- package/dist/source/matchContext.d.ts +8 -2
- package/dist/source/matchContext.jsx +7 -3
- package/dist/source/matchContext.jsx.map +1 -1
- package/dist/source/not-found.d.ts +1 -1
- package/dist/source/not-found.jsx +6 -5
- package/dist/source/not-found.jsx.map +1 -1
- package/dist/source/router.js +2 -1
- package/dist/source/router.js.map +1 -1
- package/dist/source/routerStores.d.ts +10 -0
- package/dist/source/routerStores.js +71 -0
- package/dist/source/routerStores.js.map +1 -0
- package/dist/source/ssr/RouterClient.jsx +1 -1
- package/dist/source/ssr/RouterClient.jsx.map +1 -1
- package/dist/source/ssr/renderRouterToStream.jsx +1 -1
- package/dist/source/ssr/renderRouterToStream.jsx.map +1 -1
- package/dist/source/ssr/renderRouterToString.d.ts +1 -1
- package/dist/source/ssr/renderRouterToString.jsx +2 -2
- package/dist/source/ssr/renderRouterToString.jsx.map +1 -1
- package/dist/source/useCanGoBack.d.ts +2 -1
- package/dist/source/useCanGoBack.js +4 -2
- package/dist/source/useCanGoBack.js.map +1 -1
- package/dist/source/useLoaderDeps.jsx +2 -3
- package/dist/source/useLoaderDeps.jsx.map +1 -1
- package/dist/source/useLocation.jsx +13 -3
- package/dist/source/useLocation.jsx.map +1 -1
- package/dist/source/useMatch.jsx +30 -27
- package/dist/source/useMatch.jsx.map +1 -1
- package/dist/source/useParams.jsx +1 -1
- package/dist/source/useParams.jsx.map +1 -1
- package/dist/source/useRouterState.jsx +12 -33
- package/dist/source/useRouterState.jsx.map +1 -1
- package/dist/source/useSearch.jsx +2 -1
- package/dist/source/useSearch.jsx.map +1 -1
- package/dist/source/utils.d.ts +0 -4
- package/dist/source/utils.js +0 -13
- package/dist/source/utils.js.map +1 -1
- package/package.json +2 -3
- package/skills/solid-router/SKILL.md +2 -0
- package/src/Match.tsx +351 -304
- package/src/Matches.tsx +49 -52
- package/src/Scripts.tsx +40 -41
- package/src/Transitioner.tsx +67 -66
- package/src/headContentUtils.tsx +89 -91
- package/src/link.tsx +179 -141
- package/src/matchContext.tsx +16 -7
- package/src/not-found.tsx +6 -6
- package/src/router.ts +2 -1
- package/src/routerStores.ts +107 -0
- package/src/ssr/RouterClient.tsx +1 -1
- package/src/ssr/renderRouterToStream.tsx +1 -1
- package/src/ssr/renderRouterToString.tsx +2 -2
- package/src/useCanGoBack.ts +6 -2
- package/src/useLoaderDeps.tsx +2 -3
- package/src/useLocation.tsx +18 -5
- package/src/useMatch.tsx +36 -43
- package/src/useParams.tsx +2 -3
- package/src/useRouterState.tsx +17 -41
- package/src/useSearch.tsx +2 -1
- package/src/utils.ts +0 -20
package/src/link.tsx
CHANGED
|
@@ -13,7 +13,6 @@ import {
|
|
|
13
13
|
|
|
14
14
|
import { isServer } from '@tanstack/router-core/isServer'
|
|
15
15
|
import { Dynamic } from 'solid-js/web'
|
|
16
|
-
import { useRouterState } from './useRouterState'
|
|
17
16
|
import { useRouter } from './useRouter'
|
|
18
17
|
|
|
19
18
|
import { useIntersectionObserver } from './utils'
|
|
@@ -52,8 +51,8 @@ export function useLinkProps<
|
|
|
52
51
|
const [local, rest] = Solid.splitProps(
|
|
53
52
|
Solid.mergeProps(
|
|
54
53
|
{
|
|
55
|
-
activeProps:
|
|
56
|
-
inactiveProps:
|
|
54
|
+
activeProps: STATIC_ACTIVE_PROPS_GET,
|
|
55
|
+
inactiveProps: STATIC_INACTIVE_PROPS_GET,
|
|
57
56
|
},
|
|
58
57
|
options,
|
|
59
58
|
),
|
|
@@ -123,33 +122,20 @@ export function useLinkProps<
|
|
|
123
122
|
'unsafeRelative',
|
|
124
123
|
])
|
|
125
124
|
|
|
126
|
-
const currentLocation =
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
select: (s) => {
|
|
132
|
-
const leaf = s.matches[s.matches.length - 1]
|
|
133
|
-
return {
|
|
134
|
-
search: leaf?.search,
|
|
135
|
-
hash: s.location.hash,
|
|
136
|
-
path: leaf?.pathname, // path + params
|
|
137
|
-
}
|
|
138
|
-
},
|
|
139
|
-
})
|
|
125
|
+
const currentLocation = Solid.createMemo(
|
|
126
|
+
() => router.stores.location.state,
|
|
127
|
+
undefined,
|
|
128
|
+
{ equals: (prev, next) => prev.href === next.href },
|
|
129
|
+
)
|
|
140
130
|
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
const _options = () => {
|
|
144
|
-
return {
|
|
145
|
-
...options,
|
|
146
|
-
from,
|
|
147
|
-
}
|
|
148
|
-
}
|
|
131
|
+
const _options = () => options
|
|
149
132
|
|
|
150
133
|
const next = Solid.createMemo(() => {
|
|
151
|
-
|
|
152
|
-
|
|
134
|
+
// Rebuild when inherited search/hash or the current route context changes.
|
|
135
|
+
const _fromLocation = currentLocation()
|
|
136
|
+
const options = { _fromLocation, ..._options() } as any
|
|
137
|
+
// untrack because router-core will also access stores, which are signals in solid
|
|
138
|
+
return Solid.untrack(() => router.buildLocation(options))
|
|
153
139
|
})
|
|
154
140
|
|
|
155
141
|
const hrefOption = Solid.createMemo(() => {
|
|
@@ -185,15 +171,13 @@ export function useLinkProps<
|
|
|
185
171
|
return _href.href
|
|
186
172
|
}
|
|
187
173
|
const to = _options().to
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
to.charCodeAt(1) !== 47 // but not '//'
|
|
192
|
-
if (isSafeInternal) return undefined
|
|
174
|
+
const safeInternal = isSafeInternal(to)
|
|
175
|
+
if (safeInternal) return undefined
|
|
176
|
+
if (typeof to !== 'string' || to.indexOf(':') === -1) return undefined
|
|
193
177
|
try {
|
|
194
178
|
new URL(to as any)
|
|
195
179
|
// Block dangerous protocols like javascript:, blob:, data:
|
|
196
|
-
if (isDangerousProtocol(to
|
|
180
|
+
if (isDangerousProtocol(to, router.protocolAllowlist)) {
|
|
197
181
|
if (process.env.NODE_ENV !== 'production') {
|
|
198
182
|
console.warn(`Blocked Link with dangerous protocol: ${to}`)
|
|
199
183
|
}
|
|
@@ -215,56 +199,60 @@ export function useLinkProps<
|
|
|
215
199
|
|
|
216
200
|
const isActive = Solid.createMemo(() => {
|
|
217
201
|
if (externalLink()) return false
|
|
218
|
-
|
|
202
|
+
const activeOptions = local.activeOptions
|
|
203
|
+
const current = currentLocation()
|
|
204
|
+
const nextLocation = next()
|
|
205
|
+
|
|
206
|
+
if (activeOptions?.exact) {
|
|
219
207
|
const testExact = exactPathTest(
|
|
220
|
-
|
|
221
|
-
|
|
208
|
+
current.pathname,
|
|
209
|
+
nextLocation.pathname,
|
|
222
210
|
router.basepath,
|
|
223
211
|
)
|
|
224
212
|
if (!testExact) {
|
|
225
213
|
return false
|
|
226
214
|
}
|
|
227
215
|
} else {
|
|
228
|
-
const
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
).split('/')
|
|
232
|
-
const nextPathSplit = removeTrailingSlash(
|
|
233
|
-
next()?.pathname,
|
|
216
|
+
const currentPath = removeTrailingSlash(current.pathname, router.basepath)
|
|
217
|
+
const nextPath = removeTrailingSlash(
|
|
218
|
+
nextLocation.pathname,
|
|
234
219
|
router.basepath,
|
|
235
|
-
)?.split('/')
|
|
236
|
-
|
|
237
|
-
const pathIsFuzzyEqual = nextPathSplit?.every(
|
|
238
|
-
(d, i) => d === currentPathSplit[i],
|
|
239
220
|
)
|
|
221
|
+
|
|
222
|
+
const pathIsFuzzyEqual =
|
|
223
|
+
currentPath.startsWith(nextPath) &&
|
|
224
|
+
(currentPath.length === nextPath.length ||
|
|
225
|
+
currentPath[nextPath.length] === '/')
|
|
240
226
|
if (!pathIsFuzzyEqual) {
|
|
241
227
|
return false
|
|
242
228
|
}
|
|
243
229
|
}
|
|
244
230
|
|
|
245
|
-
if (
|
|
246
|
-
const searchTest = deepEqual(
|
|
247
|
-
partial: !
|
|
248
|
-
ignoreUndefined: !
|
|
231
|
+
if (activeOptions?.includeSearch ?? true) {
|
|
232
|
+
const searchTest = deepEqual(current.search, nextLocation.search, {
|
|
233
|
+
partial: !activeOptions?.exact,
|
|
234
|
+
ignoreUndefined: !activeOptions?.explicitUndefined,
|
|
249
235
|
})
|
|
250
236
|
if (!searchTest) {
|
|
251
237
|
return false
|
|
252
238
|
}
|
|
253
239
|
}
|
|
254
240
|
|
|
255
|
-
if (
|
|
241
|
+
if (activeOptions?.includeHash) {
|
|
256
242
|
const currentHash =
|
|
257
|
-
shouldHydrateHash && !hasHydrated() ? '' :
|
|
258
|
-
return currentHash ===
|
|
243
|
+
shouldHydrateHash && !hasHydrated() ? '' : current.hash
|
|
244
|
+
return currentHash === nextLocation.hash
|
|
259
245
|
}
|
|
260
246
|
return true
|
|
261
247
|
})
|
|
262
248
|
|
|
263
249
|
const doPreload = () =>
|
|
264
|
-
router
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
250
|
+
router
|
|
251
|
+
.preloadRoute({ ..._options(), _builtLocation: next() } as any)
|
|
252
|
+
.catch((err: any) => {
|
|
253
|
+
console.warn(err)
|
|
254
|
+
console.warn(preloadWarning)
|
|
255
|
+
})
|
|
268
256
|
|
|
269
257
|
const preloadViewportIoCallback = (
|
|
270
258
|
entry: IntersectionObserverEntry | undefined,
|
|
@@ -393,100 +381,139 @@ export function useLinkProps<
|
|
|
393
381
|
}
|
|
394
382
|
}
|
|
395
383
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
384
|
+
const simpleStyling = Solid.createMemo(
|
|
385
|
+
() =>
|
|
386
|
+
local.activeProps === STATIC_ACTIVE_PROPS_GET &&
|
|
387
|
+
local.inactiveProps === STATIC_INACTIVE_PROPS_GET &&
|
|
388
|
+
local.class === undefined &&
|
|
389
|
+
local.style === undefined,
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
const onClick = createComposedHandler(() => local.onClick, handleClick)
|
|
393
|
+
const onBlur = createComposedHandler(() => local.onBlur, handleLeave)
|
|
394
|
+
const onFocus = createComposedHandler(
|
|
395
|
+
() => local.onFocus,
|
|
396
|
+
enqueueIntentPreload,
|
|
397
|
+
)
|
|
398
|
+
const onMouseEnter = createComposedHandler(
|
|
399
|
+
() => local.onMouseEnter,
|
|
400
|
+
enqueueIntentPreload,
|
|
401
|
+
)
|
|
402
|
+
const onMouseOver = createComposedHandler(
|
|
403
|
+
() => local.onMouseOver,
|
|
404
|
+
enqueueIntentPreload,
|
|
405
|
+
)
|
|
406
|
+
const onMouseLeave = createComposedHandler(
|
|
407
|
+
() => local.onMouseLeave,
|
|
408
|
+
handleLeave,
|
|
409
|
+
)
|
|
410
|
+
const onMouseOut = createComposedHandler(() => local.onMouseOut, handleLeave)
|
|
411
|
+
const onTouchStart = createComposedHandler(
|
|
412
|
+
() => local.onTouchStart,
|
|
413
|
+
handleTouchStart,
|
|
414
|
+
)
|
|
408
415
|
|
|
409
|
-
|
|
416
|
+
type ResolvedLinkStateProps = Omit<Solid.ComponentProps<'a'>, 'style'> & {
|
|
417
|
+
style?: Solid.JSX.CSSProperties
|
|
410
418
|
}
|
|
411
419
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
420
|
+
const resolvedProps = Solid.createMemo(() => {
|
|
421
|
+
const active = isActive()
|
|
422
|
+
|
|
423
|
+
const base = {
|
|
424
|
+
href: hrefOption()?.href,
|
|
425
|
+
ref: mergeRefs(setRef, _options().ref),
|
|
426
|
+
onClick,
|
|
427
|
+
onBlur,
|
|
428
|
+
onFocus,
|
|
429
|
+
onMouseEnter,
|
|
430
|
+
onMouseOver,
|
|
431
|
+
onMouseLeave,
|
|
432
|
+
onMouseOut,
|
|
433
|
+
onTouchStart,
|
|
434
|
+
disabled: !!local.disabled,
|
|
435
|
+
target: local.target,
|
|
436
|
+
...(local.disabled && STATIC_DISABLED_PROPS),
|
|
437
|
+
...(isTransitioning() && STATIC_TRANSITIONING_ATTRIBUTES),
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
if (simpleStyling()) {
|
|
441
|
+
return {
|
|
442
|
+
...base,
|
|
443
|
+
...(active && STATIC_DEFAULT_ACTIVE_ATTRIBUTES),
|
|
418
444
|
}
|
|
419
445
|
}
|
|
420
|
-
}
|
|
421
446
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
const resolvedClassName = () =>
|
|
436
|
-
[local.class, resolvedActiveProps().class, resolvedInactiveProps().class]
|
|
447
|
+
const activeProps: ResolvedLinkStateProps = active
|
|
448
|
+
? (functionalUpdate(local.activeProps as any, {}) ?? EMPTY_OBJECT)
|
|
449
|
+
: EMPTY_OBJECT
|
|
450
|
+
const inactiveProps: ResolvedLinkStateProps = active
|
|
451
|
+
? EMPTY_OBJECT
|
|
452
|
+
: functionalUpdate(local.inactiveProps, {})
|
|
453
|
+
const style = {
|
|
454
|
+
...local.style,
|
|
455
|
+
...activeProps.style,
|
|
456
|
+
...inactiveProps.style,
|
|
457
|
+
}
|
|
458
|
+
const className = [local.class, activeProps.class, inactiveProps.class]
|
|
437
459
|
.filter(Boolean)
|
|
438
460
|
.join(' ')
|
|
439
461
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
462
|
+
return {
|
|
463
|
+
...activeProps,
|
|
464
|
+
...inactiveProps,
|
|
465
|
+
...base,
|
|
466
|
+
...(Object.keys(style).length ? { style } : undefined),
|
|
467
|
+
...(className ? { class: className } : undefined),
|
|
468
|
+
...(active && STATIC_ACTIVE_ATTRIBUTES),
|
|
469
|
+
} as ResolvedLinkStateProps
|
|
444
470
|
})
|
|
445
471
|
|
|
446
|
-
return Solid.mergeProps(
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
472
|
+
return Solid.mergeProps(propsSafeToSpread, resolvedProps) as any
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
const STATIC_ACTIVE_PROPS = { class: 'active' }
|
|
476
|
+
const STATIC_ACTIVE_PROPS_GET = () => STATIC_ACTIVE_PROPS
|
|
477
|
+
const EMPTY_OBJECT = {}
|
|
478
|
+
const STATIC_INACTIVE_PROPS_GET = () => EMPTY_OBJECT
|
|
479
|
+
const STATIC_DEFAULT_ACTIVE_ATTRIBUTES = {
|
|
480
|
+
class: 'active',
|
|
481
|
+
'data-status': 'active',
|
|
482
|
+
'aria-current': 'page',
|
|
483
|
+
}
|
|
484
|
+
const STATIC_DISABLED_PROPS = {
|
|
485
|
+
role: 'link',
|
|
486
|
+
'aria-disabled': true,
|
|
487
|
+
}
|
|
488
|
+
const STATIC_ACTIVE_ATTRIBUTES = {
|
|
489
|
+
'data-status': 'active',
|
|
490
|
+
'aria-current': 'page',
|
|
491
|
+
}
|
|
492
|
+
const STATIC_TRANSITIONING_ATTRIBUTES = {
|
|
493
|
+
'data-transitioning': 'transitioning',
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/** Call a JSX.EventHandlerUnion with the event. */
|
|
497
|
+
function callHandler<T, TEvent extends Event>(
|
|
498
|
+
event: TEvent & { currentTarget: T; target: Element },
|
|
499
|
+
handler: Solid.JSX.EventHandlerUnion<T, TEvent>,
|
|
500
|
+
) {
|
|
501
|
+
if (typeof handler === 'function') {
|
|
502
|
+
handler(event)
|
|
503
|
+
} else {
|
|
504
|
+
handler[0](handler[1], event)
|
|
505
|
+
}
|
|
506
|
+
return event.defaultPrevented
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
function createComposedHandler<T, TEvent extends Event>(
|
|
510
|
+
getHandler: () => Solid.JSX.EventHandlerUnion<T, TEvent> | undefined,
|
|
511
|
+
fallback: (event: TEvent) => void,
|
|
512
|
+
) {
|
|
513
|
+
return (event: TEvent & { currentTarget: T; target: Element }) => {
|
|
514
|
+
const handler = getHandler()
|
|
515
|
+
if (!handler || !callHandler(event, handler)) fallback(event)
|
|
516
|
+
}
|
|
490
517
|
}
|
|
491
518
|
|
|
492
519
|
export type UseLinkPropsOptions<
|
|
@@ -645,8 +672,12 @@ export const Link: LinkComponent<'a'> = (props) => {
|
|
|
645
672
|
)
|
|
646
673
|
}
|
|
647
674
|
|
|
675
|
+
if (!local._asChild) {
|
|
676
|
+
return <a {...linkProps}>{children()}</a>
|
|
677
|
+
}
|
|
678
|
+
|
|
648
679
|
return (
|
|
649
|
-
<Dynamic component={local._asChild
|
|
680
|
+
<Dynamic component={local._asChild as Solid.ValidComponent} {...linkProps}>
|
|
650
681
|
{children()}
|
|
651
682
|
</Dynamic>
|
|
652
683
|
)
|
|
@@ -656,6 +687,13 @@ function isCtrlEvent(e: MouseEvent) {
|
|
|
656
687
|
return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey)
|
|
657
688
|
}
|
|
658
689
|
|
|
690
|
+
function isSafeInternal(to: unknown) {
|
|
691
|
+
if (typeof to !== 'string') return false
|
|
692
|
+
const zero = to.charCodeAt(0)
|
|
693
|
+
if (zero === 47) return to.charCodeAt(1) !== 47 // '/' but not '//'
|
|
694
|
+
return zero === 46 // '.', '..', './', '../'
|
|
695
|
+
}
|
|
696
|
+
|
|
659
697
|
export type LinkOptionsFnOptions<
|
|
660
698
|
TOptions,
|
|
661
699
|
TComp,
|
package/src/matchContext.tsx
CHANGED
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
import * as Solid from 'solid-js'
|
|
2
|
+
import type { AnyRouteMatch } from '@tanstack/router-core'
|
|
2
3
|
|
|
3
|
-
export
|
|
4
|
-
Solid.Accessor<string | undefined>
|
|
5
|
-
|
|
4
|
+
export type NearestMatchContextValue = {
|
|
5
|
+
matchId: Solid.Accessor<string | undefined>
|
|
6
|
+
routeId: Solid.Accessor<string | undefined>
|
|
7
|
+
match: Solid.Accessor<AnyRouteMatch | undefined>
|
|
8
|
+
hasPending: Solid.Accessor<boolean>
|
|
9
|
+
}
|
|
6
10
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
const defaultNearestMatchContext: NearestMatchContextValue = {
|
|
12
|
+
matchId: () => undefined,
|
|
13
|
+
routeId: () => undefined,
|
|
14
|
+
match: () => undefined,
|
|
15
|
+
hasPending: () => false,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const nearestMatchContext =
|
|
19
|
+
Solid.createContext<NearestMatchContextValue>(defaultNearestMatchContext)
|
package/src/not-found.tsx
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { isNotFound } from '@tanstack/router-core'
|
|
2
|
+
import * as Solid from 'solid-js'
|
|
2
3
|
import { CatchBoundary } from './CatchBoundary'
|
|
3
|
-
import {
|
|
4
|
-
import type * as Solid from 'solid-js'
|
|
4
|
+
import { useRouter } from './useRouter'
|
|
5
5
|
import type { NotFoundError } from '@tanstack/router-core'
|
|
6
6
|
|
|
7
7
|
export function CatchNotFound(props: {
|
|
@@ -9,14 +9,14 @@ export function CatchNotFound(props: {
|
|
|
9
9
|
onCatch?: (error: Error) => void
|
|
10
10
|
children: Solid.JSX.Element
|
|
11
11
|
}) {
|
|
12
|
+
const router = useRouter()
|
|
12
13
|
// TODO: Some way for the user to programmatically reset the not-found boundary?
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
})
|
|
14
|
+
const pathname = Solid.createMemo(() => router.stores.location.state.pathname)
|
|
15
|
+
const status = Solid.createMemo(() => router.stores.status.state)
|
|
16
16
|
|
|
17
17
|
return (
|
|
18
18
|
<CatchBoundary
|
|
19
|
-
getResetKey={() =>
|
|
19
|
+
getResetKey={() => `not-found-${pathname()}-${status()}`}
|
|
20
20
|
onCatch={(error) => {
|
|
21
21
|
if (isNotFound(error)) {
|
|
22
22
|
props.onCatch?.(error)
|
package/src/router.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { RouterCore } from '@tanstack/router-core'
|
|
2
2
|
import { createFileRoute, createLazyFileRoute } from './fileRoute'
|
|
3
|
+
import { getStoreFactory } from './routerStores'
|
|
3
4
|
import type { RouterHistory } from '@tanstack/history'
|
|
4
5
|
import type {
|
|
5
6
|
AnyRoute,
|
|
@@ -99,7 +100,7 @@ export class Router<
|
|
|
99
100
|
TDehydrated
|
|
100
101
|
>,
|
|
101
102
|
) {
|
|
102
|
-
super(options)
|
|
103
|
+
super(options, getStoreFactory)
|
|
103
104
|
}
|
|
104
105
|
}
|
|
105
106
|
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import * as Solid from 'solid-js'
|
|
2
|
+
import {
|
|
3
|
+
createNonReactiveMutableStore,
|
|
4
|
+
createNonReactiveReadonlyStore,
|
|
5
|
+
} from '@tanstack/router-core'
|
|
6
|
+
import { isServer } from '@tanstack/router-core/isServer'
|
|
7
|
+
import type {
|
|
8
|
+
AnyRoute,
|
|
9
|
+
GetStoreConfig,
|
|
10
|
+
RouterReadableStore,
|
|
11
|
+
RouterStores,
|
|
12
|
+
RouterWritableStore,
|
|
13
|
+
} from '@tanstack/router-core'
|
|
14
|
+
|
|
15
|
+
declare module '@tanstack/router-core' {
|
|
16
|
+
export interface RouterStores<in out TRouteTree extends AnyRoute> {
|
|
17
|
+
/** Maps each active routeId to the matchId of its child in the match tree. */
|
|
18
|
+
childMatchIdByRouteId: RouterReadableStore<Record<string, string>>
|
|
19
|
+
/** Maps each pending routeId to true for quick lookup. */
|
|
20
|
+
pendingRouteIds: RouterReadableStore<Record<string, boolean>>
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function initRouterStores(
|
|
25
|
+
stores: RouterStores<AnyRoute>,
|
|
26
|
+
createReadonlyStore: <TValue>(
|
|
27
|
+
read: () => TValue,
|
|
28
|
+
) => RouterReadableStore<TValue>,
|
|
29
|
+
) {
|
|
30
|
+
stores.childMatchIdByRouteId = createReadonlyStore(() => {
|
|
31
|
+
const ids = stores.matchesId.state
|
|
32
|
+
const obj: Record<string, string> = {}
|
|
33
|
+
for (let i = 0; i < ids.length - 1; i++) {
|
|
34
|
+
const parentStore = stores.activeMatchStoresById.get(ids[i]!)
|
|
35
|
+
if (parentStore?.routeId) {
|
|
36
|
+
obj[parentStore.routeId] = ids[i + 1]!
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return obj
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
stores.pendingRouteIds = createReadonlyStore(() => {
|
|
43
|
+
const ids = stores.pendingMatchesId.state
|
|
44
|
+
const obj: Record<string, boolean> = {}
|
|
45
|
+
for (const id of ids) {
|
|
46
|
+
const store = stores.pendingMatchStoresById.get(id)
|
|
47
|
+
if (store?.routeId) {
|
|
48
|
+
obj[store.routeId] = true
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return obj
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function createSolidMutableStore<TValue>(
|
|
56
|
+
initialValue: TValue,
|
|
57
|
+
): RouterWritableStore<TValue> {
|
|
58
|
+
const [signal, setSignal] = Solid.createSignal(initialValue)
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
get state() {
|
|
62
|
+
return signal()
|
|
63
|
+
},
|
|
64
|
+
setState: setSignal,
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
let finalizationRegistry: FinalizationRegistry<() => void> | null = null
|
|
69
|
+
if (typeof globalThis !== 'undefined' && 'FinalizationRegistry' in globalThis) {
|
|
70
|
+
finalizationRegistry = new FinalizationRegistry((cb) => cb())
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function createSolidReadonlyStore<TValue>(
|
|
74
|
+
read: () => TValue,
|
|
75
|
+
): RouterReadableStore<TValue> {
|
|
76
|
+
let dispose!: () => void
|
|
77
|
+
const memo = Solid.createRoot((d) => {
|
|
78
|
+
dispose = d
|
|
79
|
+
return Solid.createMemo(read)
|
|
80
|
+
})
|
|
81
|
+
const store = {
|
|
82
|
+
get state() {
|
|
83
|
+
return memo()
|
|
84
|
+
},
|
|
85
|
+
}
|
|
86
|
+
finalizationRegistry?.register(store, dispose)
|
|
87
|
+
return store
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export const getStoreFactory: GetStoreConfig = (opts) => {
|
|
91
|
+
if (isServer ?? opts.isServer) {
|
|
92
|
+
return {
|
|
93
|
+
createMutableStore: createNonReactiveMutableStore,
|
|
94
|
+
createReadonlyStore: createNonReactiveReadonlyStore,
|
|
95
|
+
batch: (fn) => fn(),
|
|
96
|
+
init: (stores) =>
|
|
97
|
+
initRouterStores(stores, createNonReactiveReadonlyStore),
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
createMutableStore: createSolidMutableStore,
|
|
103
|
+
createReadonlyStore: createSolidReadonlyStore,
|
|
104
|
+
batch: Solid.batch,
|
|
105
|
+
init: (stores) => initRouterStores(stores, createSolidReadonlyStore),
|
|
106
|
+
}
|
|
107
|
+
}
|
package/src/ssr/RouterClient.tsx
CHANGED
|
@@ -11,7 +11,7 @@ const Dummy = (props: { children?: JSXElement }) => <>{props.children}</>
|
|
|
11
11
|
|
|
12
12
|
export function RouterClient(props: { router: AnyRouter }) {
|
|
13
13
|
if (!hydrationPromise) {
|
|
14
|
-
if (!props.router.state.
|
|
14
|
+
if (!props.router.stores.matchesId.state.length) {
|
|
15
15
|
hydrationPromise = hydrate(props.router)
|
|
16
16
|
} else {
|
|
17
17
|
hydrationPromise = Promise.resolve()
|
|
@@ -3,7 +3,7 @@ import { makeSsrSerovalPlugin } from '@tanstack/router-core'
|
|
|
3
3
|
import type { AnyRouter } from '@tanstack/router-core'
|
|
4
4
|
import type { JSXElement } from 'solid-js'
|
|
5
5
|
|
|
6
|
-
export const renderRouterToString =
|
|
6
|
+
export const renderRouterToString = ({
|
|
7
7
|
router,
|
|
8
8
|
responseHeaders,
|
|
9
9
|
children,
|
|
@@ -32,7 +32,7 @@ export const renderRouterToString = async ({
|
|
|
32
32
|
html = html.replace(`</body>`, () => `${injectedHtml}</body>`)
|
|
33
33
|
}
|
|
34
34
|
return new Response(`<!DOCTYPE html>${html}`, {
|
|
35
|
-
status: router.
|
|
35
|
+
status: router.stores.statusCode.state,
|
|
36
36
|
headers: responseHeaders,
|
|
37
37
|
})
|
|
38
38
|
} catch (error) {
|
package/src/useCanGoBack.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as Solid from 'solid-js'
|
|
2
|
+
import { useRouter } from './useRouter'
|
|
2
3
|
|
|
3
4
|
export function useCanGoBack() {
|
|
4
|
-
|
|
5
|
+
const router = useRouter()
|
|
6
|
+
return Solid.createMemo(
|
|
7
|
+
() => router.stores.location.state.state.__TSR_index !== 0,
|
|
8
|
+
)
|
|
5
9
|
}
|
package/src/useLoaderDeps.tsx
CHANGED
|
@@ -37,11 +37,10 @@ export function useLoaderDeps<
|
|
|
37
37
|
>(
|
|
38
38
|
opts: UseLoaderDepsOptions<TRouter, TFrom, TSelected>,
|
|
39
39
|
): Accessor<UseLoaderDepsResult<TRouter, TFrom, TSelected>> {
|
|
40
|
-
const { select, ...rest } = opts
|
|
41
40
|
return useMatch({
|
|
42
|
-
...
|
|
41
|
+
...opts,
|
|
43
42
|
select: (s) => {
|
|
44
|
-
return select ? select(s.loaderDeps) : s.loaderDeps
|
|
43
|
+
return opts.select ? opts.select(s.loaderDeps) : s.loaderDeps
|
|
45
44
|
},
|
|
46
45
|
}) as Accessor<UseLoaderDepsResult<TRouter, TFrom, TSelected>>
|
|
47
46
|
}
|