@tanstack/solid-router 2.0.0-alpha.6 → 2.0.0-alpha.7
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/Asset.cjs +2 -2
- package/dist/cjs/Asset.cjs.map +1 -1
- package/dist/cjs/HeadContent.cjs +11 -1
- package/dist/cjs/HeadContent.cjs.map +1 -1
- package/dist/cjs/HeadContent.dev.cjs +11 -1
- package/dist/cjs/HeadContent.dev.cjs.map +1 -1
- package/dist/cjs/Match.cjs +265 -248
- package/dist/cjs/Match.cjs.map +1 -1
- package/dist/cjs/Match.d.cts +1 -3
- package/dist/cjs/Matches.cjs +35 -34
- package/dist/cjs/Matches.cjs.map +1 -1
- package/dist/cjs/RouterProvider.cjs +12 -8
- package/dist/cjs/RouterProvider.cjs.map +1 -1
- package/dist/cjs/RouterProvider.d.cts +1 -1
- package/dist/cjs/Scripts.cjs +23 -12
- package/dist/cjs/Scripts.cjs.map +1 -1
- package/dist/cjs/Scripts.d.cts +2 -1
- package/dist/cjs/Transitioner.cjs +55 -34
- package/dist/cjs/Transitioner.cjs.map +1 -1
- package/dist/cjs/headContentUtils.cjs +26 -23
- package/dist/cjs/headContentUtils.cjs.map +1 -1
- package/dist/cjs/headContentUtils.d.cts +2 -1
- package/dist/cjs/index.cjs +1 -1
- package/dist/cjs/index.dev.cjs +1 -1
- package/dist/cjs/link.cjs +143 -101
- 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 +75 -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 +1 -1
- package/dist/cjs/ssr/renderRouterToString.cjs.map +1 -1
- package/dist/cjs/useBlocker.cjs +12 -3
- package/dist/cjs/useBlocker.cjs.map +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 +27 -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 -30
- 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 +3 -17
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/cjs/utils.d.cts +0 -5
- package/dist/esm/Asset.js +6 -6
- package/dist/esm/Asset.js.map +1 -1
- package/dist/esm/HeadContent.dev.js +12 -2
- package/dist/esm/HeadContent.dev.js.map +1 -1
- package/dist/esm/HeadContent.js +12 -2
- package/dist/esm/HeadContent.js.map +1 -1
- package/dist/esm/Match.d.ts +1 -3
- package/dist/esm/Match.js +267 -250
- package/dist/esm/Match.js.map +1 -1
- package/dist/esm/Matches.js +40 -39
- package/dist/esm/Matches.js.map +1 -1
- package/dist/esm/RouterProvider.d.ts +1 -1
- package/dist/esm/RouterProvider.js +10 -7
- package/dist/esm/RouterProvider.js.map +1 -1
- package/dist/esm/ScriptOnce.js +2 -2
- package/dist/esm/ScriptOnce.js.map +1 -1
- package/dist/esm/Scripts.d.ts +2 -1
- package/dist/esm/Scripts.js +21 -11
- package/dist/esm/Scripts.js.map +1 -1
- package/dist/esm/Transitioner.js +56 -35
- package/dist/esm/Transitioner.js.map +1 -1
- package/dist/esm/headContentUtils.d.ts +2 -1
- package/dist/esm/headContentUtils.js +26 -23
- 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 +146 -104
- 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 +73 -0
- package/dist/esm/routerStores.js.map +1 -0
- package/dist/esm/scroll-restoration.js +2 -2
- package/dist/esm/scroll-restoration.js.map +1 -1
- 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.js +1 -1
- package/dist/esm/ssr/renderRouterToString.js.map +1 -1
- package/dist/esm/useBlocker.js +12 -3
- package/dist/esm/useBlocker.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 +28 -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 +11 -30
- 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 -5
- package/dist/esm/utils.js +4 -17
- package/dist/esm/utils.js.map +1 -1
- package/dist/source/Asset.jsx +3 -3
- package/dist/source/Asset.jsx.map +1 -1
- package/dist/source/HeadContent.dev.jsx +5 -1
- package/dist/source/HeadContent.dev.jsx.map +1 -1
- package/dist/source/HeadContent.jsx +5 -1
- package/dist/source/HeadContent.jsx.map +1 -1
- package/dist/source/Match.d.ts +1 -3
- package/dist/source/Match.jsx +260 -264
- package/dist/source/Match.jsx.map +1 -1
- package/dist/source/Matches.jsx +46 -46
- package/dist/source/Matches.jsx.map +1 -1
- package/dist/source/RouterProvider.d.ts +1 -1
- package/dist/source/RouterProvider.jsx +13 -9
- package/dist/source/RouterProvider.jsx.map +1 -1
- package/dist/source/Scripts.d.ts +2 -1
- package/dist/source/Scripts.jsx +46 -47
- package/dist/source/Scripts.jsx.map +1 -1
- package/dist/source/Transitioner.jsx +78 -42
- package/dist/source/Transitioner.jsx.map +1 -1
- package/dist/source/headContentUtils.d.ts +2 -1
- package/dist/source/headContentUtils.jsx +79 -80
- package/dist/source/headContentUtils.jsx.map +1 -1
- package/dist/source/link.jsx +145 -112
- 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 +82 -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.jsx +1 -1
- package/dist/source/ssr/renderRouterToString.jsx.map +1 -1
- package/dist/source/useBlocker.jsx +19 -8
- package/dist/source/useBlocker.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 +33 -23
- 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 +14 -55
- 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 -5
- package/dist/source/utils.js +2 -15
- package/dist/source/utils.js.map +1 -1
- package/package.json +2 -2
- package/skills/solid-router/SKILL.md +2 -0
- package/src/Asset.tsx +3 -3
- package/src/HeadContent.dev.tsx +10 -1
- package/src/HeadContent.tsx +10 -1
- package/src/Match.tsx +395 -349
- package/src/Matches.tsx +55 -54
- package/src/RouterProvider.tsx +13 -10
- package/src/Scripts.tsx +55 -54
- package/src/Transitioner.tsx +101 -58
- package/src/headContentUtils.tsx +104 -96
- package/src/link.tsx +188 -146
- package/src/matchContext.tsx +16 -7
- package/src/not-found.tsx +6 -6
- package/src/router.ts +2 -1
- package/src/routerStores.ts +119 -0
- package/src/ssr/RouterClient.tsx +1 -1
- package/src/ssr/renderRouterToStream.tsx +1 -1
- package/src/ssr/renderRouterToString.tsx +1 -1
- package/src/useBlocker.tsx +80 -63
- package/src/useCanGoBack.ts +6 -2
- package/src/useLoaderDeps.tsx +2 -3
- package/src/useLocation.tsx +18 -5
- package/src/useMatch.tsx +37 -38
- package/src/useParams.tsx +2 -3
- package/src/useRouterState.tsx +21 -67
- package/src/useSearch.tsx +2 -1
- package/src/utils.ts +2 -24
package/src/link.tsx
CHANGED
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
|
|
12
12
|
import { isServer } from '@tanstack/router-core/isServer'
|
|
13
13
|
import { Dynamic } from '@solidjs/web'
|
|
14
|
-
import { useRouterState } from './useRouterState'
|
|
15
14
|
import { useRouter } from './useRouter'
|
|
16
15
|
|
|
17
16
|
import { useIntersectionObserver } from './utils'
|
|
@@ -75,8 +74,8 @@ export function useLinkProps<
|
|
|
75
74
|
const [local, rest] = splitProps(
|
|
76
75
|
Solid.merge(
|
|
77
76
|
{
|
|
78
|
-
activeProps:
|
|
79
|
-
inactiveProps:
|
|
77
|
+
activeProps: STATIC_ACTIVE_PROPS_GET,
|
|
78
|
+
inactiveProps: STATIC_INACTIVE_PROPS_GET,
|
|
80
79
|
},
|
|
81
80
|
options,
|
|
82
81
|
),
|
|
@@ -118,34 +117,20 @@ export function useLinkProps<
|
|
|
118
117
|
'unsafeRelative',
|
|
119
118
|
] as any)
|
|
120
119
|
|
|
121
|
-
const currentLocation =
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
select: (s) => {
|
|
127
|
-
const leaf = s.matches[s.matches.length - 1]
|
|
128
|
-
return {
|
|
129
|
-
search: leaf?.search,
|
|
130
|
-
hash: s.location.hash,
|
|
131
|
-
path: leaf?.pathname, // path + params
|
|
132
|
-
}
|
|
133
|
-
},
|
|
134
|
-
})
|
|
120
|
+
const currentLocation = Solid.createMemo(
|
|
121
|
+
() => router.stores.location.state,
|
|
122
|
+
undefined,
|
|
123
|
+
{ equals: (prev, next) => prev?.href === next?.href },
|
|
124
|
+
)
|
|
135
125
|
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
const _options = () => {
|
|
139
|
-
const result = {
|
|
140
|
-
...options,
|
|
141
|
-
from,
|
|
142
|
-
}
|
|
143
|
-
return result
|
|
144
|
-
}
|
|
126
|
+
const _options = () => options
|
|
145
127
|
|
|
146
128
|
const next = Solid.createMemo(() => {
|
|
147
|
-
|
|
148
|
-
|
|
129
|
+
// Rebuild when inherited search/hash or the current route context changes.
|
|
130
|
+
const _fromLocation = currentLocation()
|
|
131
|
+
const options = { _fromLocation, ..._options() } as any
|
|
132
|
+
// untrack because router-core will also access stores, which are signals in solid
|
|
133
|
+
return Solid.untrack(() => router.buildLocation(options))
|
|
149
134
|
})
|
|
150
135
|
|
|
151
136
|
const hrefOption = Solid.createMemo(() => {
|
|
@@ -181,15 +166,13 @@ export function useLinkProps<
|
|
|
181
166
|
return _href.href
|
|
182
167
|
}
|
|
183
168
|
const to = _options().to
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
to.charCodeAt(1) !== 47 // but not '//'
|
|
188
|
-
if (isSafeInternal) return undefined
|
|
169
|
+
const safeInternal = isSafeInternal(to)
|
|
170
|
+
if (safeInternal) return undefined
|
|
171
|
+
if (typeof to !== 'string' || to.indexOf(':') === -1) return undefined
|
|
189
172
|
try {
|
|
190
173
|
new URL(to as any)
|
|
191
174
|
// Block dangerous protocols like javascript:, blob:, data:
|
|
192
|
-
if (isDangerousProtocol(to
|
|
175
|
+
if (isDangerousProtocol(to, router.protocolAllowlist)) {
|
|
193
176
|
if (process.env.NODE_ENV !== 'production') {
|
|
194
177
|
console.warn(`Blocked Link with dangerous protocol: ${to}`)
|
|
195
178
|
}
|
|
@@ -211,56 +194,60 @@ export function useLinkProps<
|
|
|
211
194
|
|
|
212
195
|
const isActive = Solid.createMemo(() => {
|
|
213
196
|
if (externalLink()) return false
|
|
214
|
-
|
|
197
|
+
const activeOptions = local.activeOptions
|
|
198
|
+
const current = currentLocation()
|
|
199
|
+
const nextLocation = next()
|
|
200
|
+
|
|
201
|
+
if (activeOptions?.exact) {
|
|
215
202
|
const testExact = exactPathTest(
|
|
216
|
-
|
|
217
|
-
|
|
203
|
+
current.pathname,
|
|
204
|
+
nextLocation.pathname,
|
|
218
205
|
router.basepath,
|
|
219
206
|
)
|
|
220
207
|
if (!testExact) {
|
|
221
208
|
return false
|
|
222
209
|
}
|
|
223
210
|
} else {
|
|
224
|
-
const
|
|
225
|
-
|
|
211
|
+
const currentPath = removeTrailingSlash(current.pathname, router.basepath)
|
|
212
|
+
const nextPath = removeTrailingSlash(
|
|
213
|
+
nextLocation.pathname,
|
|
226
214
|
router.basepath,
|
|
227
|
-
).split('/')
|
|
228
|
-
const nextPathSplit = removeTrailingSlash(
|
|
229
|
-
next()?.pathname,
|
|
230
|
-
router.basepath,
|
|
231
|
-
)?.split('/')
|
|
232
|
-
|
|
233
|
-
const pathIsFuzzyEqual = nextPathSplit?.every(
|
|
234
|
-
(d, i) => d === currentPathSplit[i],
|
|
235
215
|
)
|
|
216
|
+
|
|
217
|
+
const pathIsFuzzyEqual =
|
|
218
|
+
currentPath.startsWith(nextPath) &&
|
|
219
|
+
(currentPath.length === nextPath.length ||
|
|
220
|
+
currentPath[nextPath.length] === '/')
|
|
236
221
|
if (!pathIsFuzzyEqual) {
|
|
237
222
|
return false
|
|
238
223
|
}
|
|
239
224
|
}
|
|
240
225
|
|
|
241
|
-
if (
|
|
242
|
-
const searchTest = deepEqual(
|
|
243
|
-
partial: !
|
|
244
|
-
ignoreUndefined: !
|
|
226
|
+
if (activeOptions?.includeSearch ?? true) {
|
|
227
|
+
const searchTest = deepEqual(current.search, nextLocation.search, {
|
|
228
|
+
partial: !activeOptions?.exact,
|
|
229
|
+
ignoreUndefined: !activeOptions?.explicitUndefined,
|
|
245
230
|
})
|
|
246
231
|
if (!searchTest) {
|
|
247
232
|
return false
|
|
248
233
|
}
|
|
249
234
|
}
|
|
250
235
|
|
|
251
|
-
if (
|
|
236
|
+
if (activeOptions?.includeHash) {
|
|
252
237
|
const currentHash =
|
|
253
|
-
shouldHydrateHash && !hasHydrated() ? '' :
|
|
254
|
-
return currentHash ===
|
|
238
|
+
shouldHydrateHash && !hasHydrated() ? '' : current.hash
|
|
239
|
+
return currentHash === nextLocation.hash
|
|
255
240
|
}
|
|
256
241
|
return true
|
|
257
242
|
})
|
|
258
243
|
|
|
259
244
|
const doPreload = () =>
|
|
260
|
-
router
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
245
|
+
router
|
|
246
|
+
.preloadRoute({ ..._options(), _builtLocation: next() } as any)
|
|
247
|
+
.catch((err: any) => {
|
|
248
|
+
console.warn(err)
|
|
249
|
+
console.warn(preloadWarning)
|
|
250
|
+
})
|
|
264
251
|
|
|
265
252
|
const preloadViewportIoCallback = (
|
|
266
253
|
entry: IntersectionObserverEntry | undefined,
|
|
@@ -290,17 +277,18 @@ export function useLinkProps<
|
|
|
290
277
|
return
|
|
291
278
|
}
|
|
292
279
|
if (!local.disabled && preloadValue === 'render') {
|
|
293
|
-
doPreload()
|
|
280
|
+
Solid.untrack(() => doPreload())
|
|
294
281
|
hasRenderFetched = true
|
|
295
282
|
}
|
|
296
283
|
})
|
|
297
284
|
|
|
298
285
|
if (Solid.untrack(externalLink)) {
|
|
286
|
+
const externalHref = Solid.untrack(externalLink)
|
|
299
287
|
return Solid.merge(
|
|
300
288
|
propsSafeToSpread,
|
|
301
289
|
{
|
|
302
290
|
// ref: mergeRefs(setRef, _options().ref),
|
|
303
|
-
href:
|
|
291
|
+
href: externalHref,
|
|
304
292
|
},
|
|
305
293
|
splitProps(local, [
|
|
306
294
|
'target',
|
|
@@ -337,11 +325,15 @@ export function useLinkProps<
|
|
|
337
325
|
) {
|
|
338
326
|
e.preventDefault()
|
|
339
327
|
|
|
340
|
-
|
|
328
|
+
Solid.runWithOwner(null, () => {
|
|
329
|
+
setIsTransitioning(true)
|
|
330
|
+
})
|
|
341
331
|
|
|
342
332
|
const unsub = router.subscribe('onResolved', () => {
|
|
343
333
|
unsub()
|
|
344
|
-
|
|
334
|
+
Solid.runWithOwner(null, () => {
|
|
335
|
+
setIsTransitioning(false)
|
|
336
|
+
})
|
|
345
337
|
})
|
|
346
338
|
|
|
347
339
|
// All is well? Navigate!
|
|
@@ -395,100 +387,139 @@ export function useLinkProps<
|
|
|
395
387
|
}
|
|
396
388
|
}
|
|
397
389
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
390
|
+
const simpleStyling = Solid.createMemo(
|
|
391
|
+
() =>
|
|
392
|
+
local.activeProps === STATIC_ACTIVE_PROPS_GET &&
|
|
393
|
+
local.inactiveProps === STATIC_INACTIVE_PROPS_GET &&
|
|
394
|
+
local.class === undefined &&
|
|
395
|
+
local.style === undefined,
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
const onClick = createComposedHandler(() => local.onClick, handleClick)
|
|
399
|
+
const onBlur = createComposedHandler(() => local.onBlur, handleLeave)
|
|
400
|
+
const onFocus = createComposedHandler(
|
|
401
|
+
() => local.onFocus,
|
|
402
|
+
enqueueIntentPreload,
|
|
403
|
+
)
|
|
404
|
+
const onMouseEnter = createComposedHandler(
|
|
405
|
+
() => local.onMouseEnter,
|
|
406
|
+
enqueueIntentPreload,
|
|
407
|
+
)
|
|
408
|
+
const onMouseOver = createComposedHandler(
|
|
409
|
+
() => local.onMouseOver,
|
|
410
|
+
enqueueIntentPreload,
|
|
411
|
+
)
|
|
412
|
+
const onMouseLeave = createComposedHandler(
|
|
413
|
+
() => local.onMouseLeave,
|
|
414
|
+
handleLeave,
|
|
415
|
+
)
|
|
416
|
+
const onMouseOut = createComposedHandler(() => local.onMouseOut, handleLeave)
|
|
417
|
+
const onTouchStart = createComposedHandler(
|
|
418
|
+
() => local.onTouchStart,
|
|
419
|
+
handleTouchStart,
|
|
420
|
+
)
|
|
410
421
|
|
|
411
|
-
|
|
422
|
+
type ResolvedLinkStateProps = Omit<Solid.ComponentProps<'a'>, 'style'> & {
|
|
423
|
+
style?: Solid.JSX.CSSProperties
|
|
412
424
|
}
|
|
413
425
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
426
|
+
const resolvedProps = Solid.createMemo(() => {
|
|
427
|
+
const active = isActive()
|
|
428
|
+
|
|
429
|
+
const base = {
|
|
430
|
+
href: hrefOption()?.href,
|
|
431
|
+
ref: mergeRefs(setRef, _options().ref as any),
|
|
432
|
+
onClick,
|
|
433
|
+
onBlur,
|
|
434
|
+
onFocus,
|
|
435
|
+
onMouseEnter,
|
|
436
|
+
onMouseOver,
|
|
437
|
+
onMouseLeave,
|
|
438
|
+
onMouseOut,
|
|
439
|
+
onTouchStart,
|
|
440
|
+
disabled: !!local.disabled,
|
|
441
|
+
target: local.target,
|
|
442
|
+
...(local.disabled && STATIC_DISABLED_PROPS),
|
|
443
|
+
...(isTransitioning() && STATIC_TRANSITIONING_ATTRIBUTES),
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
if (simpleStyling()) {
|
|
447
|
+
return {
|
|
448
|
+
...base,
|
|
449
|
+
...(active && STATIC_DEFAULT_ACTIVE_ATTRIBUTES),
|
|
420
450
|
}
|
|
421
451
|
}
|
|
422
|
-
}
|
|
423
452
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
const resolvedClassName = () =>
|
|
438
|
-
[local.class, resolvedActiveProps().class, resolvedInactiveProps().class]
|
|
453
|
+
const activeProps: ResolvedLinkStateProps = active
|
|
454
|
+
? (functionalUpdate(local.activeProps as any, {}) ?? EMPTY_OBJECT)
|
|
455
|
+
: EMPTY_OBJECT
|
|
456
|
+
const inactiveProps: ResolvedLinkStateProps = active
|
|
457
|
+
? EMPTY_OBJECT
|
|
458
|
+
: functionalUpdate(local.inactiveProps, {})
|
|
459
|
+
const style = {
|
|
460
|
+
...local.style,
|
|
461
|
+
...activeProps.style,
|
|
462
|
+
...inactiveProps.style,
|
|
463
|
+
}
|
|
464
|
+
const className = [local.class, activeProps.class, inactiveProps.class]
|
|
439
465
|
.filter(Boolean)
|
|
440
466
|
.join(' ')
|
|
441
467
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
468
|
+
return {
|
|
469
|
+
...activeProps,
|
|
470
|
+
...inactiveProps,
|
|
471
|
+
...base,
|
|
472
|
+
...(Object.keys(style).length ? { style } : undefined),
|
|
473
|
+
...(className ? { class: className } : undefined),
|
|
474
|
+
...(active && STATIC_ACTIVE_ATTRIBUTES),
|
|
475
|
+
} as ResolvedLinkStateProps
|
|
446
476
|
})
|
|
447
477
|
|
|
448
|
-
return Solid.merge(
|
|
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
|
-
|
|
490
|
-
|
|
491
|
-
|
|
478
|
+
return Solid.merge(propsSafeToSpread, resolvedProps) as any
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
const STATIC_ACTIVE_PROPS = { class: 'active' }
|
|
482
|
+
const STATIC_ACTIVE_PROPS_GET = () => STATIC_ACTIVE_PROPS
|
|
483
|
+
const EMPTY_OBJECT = {}
|
|
484
|
+
const STATIC_INACTIVE_PROPS_GET = () => EMPTY_OBJECT
|
|
485
|
+
const STATIC_DEFAULT_ACTIVE_ATTRIBUTES = {
|
|
486
|
+
class: 'active',
|
|
487
|
+
'data-status': 'active',
|
|
488
|
+
'aria-current': 'page',
|
|
489
|
+
}
|
|
490
|
+
const STATIC_DISABLED_PROPS = {
|
|
491
|
+
role: 'link',
|
|
492
|
+
'aria-disabled': 'true',
|
|
493
|
+
}
|
|
494
|
+
const STATIC_ACTIVE_ATTRIBUTES = {
|
|
495
|
+
'data-status': 'active',
|
|
496
|
+
'aria-current': 'page',
|
|
497
|
+
}
|
|
498
|
+
const STATIC_TRANSITIONING_ATTRIBUTES = {
|
|
499
|
+
'data-transitioning': 'transitioning',
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/** Call a JSX.EventHandlerUnion with the event. */
|
|
503
|
+
function callHandler<T, TEvent extends Event>(
|
|
504
|
+
event: TEvent & { currentTarget: T; target: Element },
|
|
505
|
+
handler: Solid.JSX.EventHandlerUnion<T, TEvent>,
|
|
506
|
+
) {
|
|
507
|
+
if (typeof handler === 'function') {
|
|
508
|
+
handler(event)
|
|
509
|
+
} else {
|
|
510
|
+
handler[0](handler[1], event)
|
|
511
|
+
}
|
|
512
|
+
return event.defaultPrevented
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
function createComposedHandler<T, TEvent extends Event>(
|
|
516
|
+
getHandler: () => Solid.JSX.EventHandlerUnion<T, TEvent> | undefined,
|
|
517
|
+
fallback: (event: TEvent) => void,
|
|
518
|
+
) {
|
|
519
|
+
return (event: TEvent & { currentTarget: T; target: Element }) => {
|
|
520
|
+
const handler = getHandler()
|
|
521
|
+
if (!handler || !callHandler(event, handler)) fallback(event)
|
|
522
|
+
}
|
|
492
523
|
}
|
|
493
524
|
|
|
494
525
|
export type UseLinkPropsOptions<
|
|
@@ -653,8 +684,12 @@ export const Link: LinkComponent<'a'> = (props) => {
|
|
|
653
684
|
)
|
|
654
685
|
}
|
|
655
686
|
|
|
687
|
+
if (!local._asChild) {
|
|
688
|
+
return <a {...linkProps}>{children()}</a>
|
|
689
|
+
}
|
|
690
|
+
|
|
656
691
|
return (
|
|
657
|
-
<Dynamic component={local._asChild
|
|
692
|
+
<Dynamic component={local._asChild as Solid.ValidComponent} {...linkProps}>
|
|
658
693
|
{children()}
|
|
659
694
|
</Dynamic>
|
|
660
695
|
)
|
|
@@ -664,6 +699,13 @@ function isCtrlEvent(e: MouseEvent) {
|
|
|
664
699
|
return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey)
|
|
665
700
|
}
|
|
666
701
|
|
|
702
|
+
function isSafeInternal(to: unknown) {
|
|
703
|
+
if (typeof to !== 'string') return false
|
|
704
|
+
const zero = to.charCodeAt(0)
|
|
705
|
+
if (zero === 47) return to.charCodeAt(1) !== 47 // '/' but not '//'
|
|
706
|
+
return zero === 46 // '.', '..', './', '../'
|
|
707
|
+
}
|
|
708
|
+
|
|
667
709
|
export type LinkOptionsFnOptions<
|
|
668
710
|
TOptions,
|
|
669
711
|
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,119 @@
|
|
|
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 as any)
|
|
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
|
+
let depth = 0
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
createMutableStore: createSolidMutableStore,
|
|
105
|
+
createReadonlyStore: createSolidReadonlyStore,
|
|
106
|
+
|
|
107
|
+
batch: (fn) => {
|
|
108
|
+
depth++
|
|
109
|
+
fn()
|
|
110
|
+
depth--
|
|
111
|
+
if (depth === 0) {
|
|
112
|
+
try {
|
|
113
|
+
Solid.flush()
|
|
114
|
+
} catch {}
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
init: (stores) => initRouterStores(stores, createSolidReadonlyStore),
|
|
118
|
+
}
|
|
119
|
+
}
|
package/src/ssr/RouterClient.tsx
CHANGED
|
@@ -7,7 +7,7 @@ let hydrationPromise: Promise<void | Array<Array<void>>> | undefined
|
|
|
7
7
|
|
|
8
8
|
export function RouterClient(props: { router: AnyRouter }) {
|
|
9
9
|
if (!hydrationPromise) {
|
|
10
|
-
if (!props.router.state.
|
|
10
|
+
if (!props.router.stores.matchesId.state.length) {
|
|
11
11
|
hydrationPromise = hydrate(props.router)
|
|
12
12
|
} else {
|
|
13
13
|
hydrationPromise = Promise.resolve()
|
|
@@ -32,7 +32,7 @@ export const renderRouterToString = ({
|
|
|
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) {
|