encodeParam,
} from '../../encoding'
import type { MatcherParamsFormatted } from './matchers/matcher-pattern'
-import { _RouteRecordProps } from '../../typed-routes'
-import { NEW_MatcherDynamicRecord } from './resolver-dynamic'
+import type { _RouteRecordProps } from '../../typed-routes'
+import type { NEW_MatcherDynamicRecord } from './resolver-dynamic'
+import type { LocationNormalized } from '../../location'
/**
* Allowed types for a matcher name.
* - `TMatcherRecordRaw` represents the raw record type passed to {@link addMatcher}.
* - `TMatcherRecord` represents the normalized record type returned by {@link getRecords}.
*/
-export interface NEW_RouterResolver_Base<TRecord> {
+export interface EXPERIMENTAL_Resolver_Base<TRecord> {
/**
* Resolves an absolute location (like `/path/to/somewhere`).
*
}
/**
- * Allowed location objects to be passed to {@link NEW_RouterResolver['resolve']}
+ * Allowed location objects to be passed to {@link EXPERIMENTAL_Resolver_Base['resolve']}
*/
export type MatcherLocationRaw =
// | `/${string}`
| ResolverLocationAsRelative
/**
- * Returned location object by {@link NEW_RouterResolver['resolve']}.
+ * Returned location object by {@link EXPERIMENTAL_Resolver_Base['resolve']}.
* It contains the resolved name, params, query, hash, and matched records.
*/
-export interface ResolverLocationResolved<TMatched> {
+export interface ResolverLocationResolved<TMatched> extends LocationNormalized {
name: RecordName
params: MatcherParamsFormatted
- fullPath: string
- path: string
- query: LocationQuery
- hash: string
-
matched: TMatched[]
}
name: __DEV__ ? Symbol('no-match') : Symbol(),
params: {},
matched: [],
-} satisfies Omit<
- ResolverLocationResolved<unknown>,
- 'path' | 'hash' | 'query' | 'fullPath'
->
+} satisfies Omit<ResolverLocationResolved<never>, keyof LocationNormalized>
/**
* Normalized version of a {@link NEW_MatcherRecordRaw} record.
RouteRecordNameGeneric,
} from '../typed-routes'
import {
- isRouteLocation,
Lazy,
RawRouteComponent,
RouteLocationOptions,
EXPERIMENTAL_ResolverRecord_Matchable,
EXPERIMENTAL_ResolverStatic,
} from './route-resolver/resolver-static'
+import {
+ ResolverLocationAsNamed,
+ ResolverLocationAsPathRelative,
+ ResolverLocationAsRelative,
+ ResolverLocationResolved,
+} from './route-resolver/resolver-abstract'
/**
* resolve, reject arguments of Promise constructor
: to
}
+ // NOTE: to support multiple overloads
+ type TRecord = EXPERIMENTAL_RouteRecordNormalized
+ type _resolveArgs =
+ // TODO: is it worth suppoting the absolute location variants?
+ // | [absoluteLocation: `/${string}`, currentLocation?: undefined]
+ | [
+ relativeLocation: string,
+ // FIXME: use router locations
+ currentLocation?: ResolverLocationResolved<TRecord>,
+ ]
+ // | [
+ // absoluteLocation: ResolverLocationAsPathAbsolute,
+ // // Same as above
+ // // currentLocation?: NEW_LocationResolved<TRecord> | undefined
+ // currentLocation?: undefined,
+ // ]
+ | [
+ relativeLocation: ResolverLocationAsPathRelative,
+ currentLocation: ResolverLocationResolved<TRecord>,
+ ]
+ | [
+ location: ResolverLocationAsNamed,
+ // Same as above
+ // currentLocation?: NEW_LocationResolved<TRecord> | undefined
+ currentLocation?: undefined,
+ ]
+ | [
+ relativeLocation: ResolverLocationAsRelative,
+ currentLocation: ResolverLocationResolved<TRecord>,
+ ]
+ | [resolvedLocation: RouteLocationResolved, currentLocation?: undefined]
+
function resolve(
- rawLocation: RouteLocationRaw,
- currentLocation?: RouteLocationNormalizedLoaded
+ ...[to, currentLocation]: _resolveArgs
): RouteLocationResolved {
// const resolve: Router['resolve'] = (rawLocation: RouteLocationRaw, currentLocation) => {
// const objectLocation = routerLocationAsObject(rawLocation)
// we create a copy to modify it later
// TODO: in the experimental version, allow configuring this
currentLocation =
+ // TODO: || currentRoute.value never evaluated
currentLocation && assign({}, currentLocation || currentRoute.value)
// currentLocation = assign({}, currentLocation || currentRoute.value)
- if (__DEV__) {
- if (!isRouteLocation(rawLocation)) {
- warn(
- `router.resolve() was passed an invalid location. This will fail in production.\n- Location:`,
- rawLocation
- )
- return resolve({})
- }
-
- if (
- typeof rawLocation === 'object' &&
- rawLocation.hash?.startsWith('#')
- ) {
- warn(
- `A \`hash\` should always start with the character "#". Replace "${rawLocation.hash}" with "#${rawLocation.hash}".`
- )
- }
- }
-
- // FIXME: is this achieved by matchers?
+ // FIXME: should this be achieved by matchers?
// remove any nullish param
// if ('params' in rawLocation) {
// const targetParams = assign({}, rawLocation.params)
// }
const matchedRoute = resolver.resolve(
- // FIXME: incompatible types
- rawLocation as any,
+ // @ts-expect-error FIXME: incompatible types
+ to,
// FIXME: incompatible `matched` requires casting
- currentLocation as any
+ currentLocation
)
const href = routerHistory.createHref(matchedRoute.fullPath)
if (href.startsWith('//')) {
warn(
`Location ${JSON.stringify(
- rawLocation
+ to
)} resolved to "${href}". A resolved location cannot start with multiple slashes.`
)
}
if (!matchedRoute.matched.length) {
- warn(`No match found for location with path "${rawLocation}"`)
+ warn(`No match found for location with path "${to}"`)
}
}
// matchedRoute is always a new object
- // @ts-expect-error: the `matched` property is different
+ // @ts-expect-error: FIXME: the `matched` property is different
return assign(matchedRoute, {
redirectedFrom: undefined,
href,
}
}
- function push(to: RouteLocationRaw) {
- return pushWithRedirect(to)
- }
+ const push = (...args: _resolveArgs) => pushWithRedirect(resolve(...args))
- function replace(to: RouteLocationRaw) {
- return pushWithRedirect(to, true)
- }
+ const replace = (...args: _resolveArgs) =>
+ pushWithRedirect(resolve(...args), true)
function handleRedirectRecord(to: RouteLocation): RouteLocationRaw | void {
const lastMatched = to.matched[to.matched.length - 1]
}
function pushWithRedirect(
- to: RouteLocationRaw | RouteLocation,
- _replace?: boolean,
+ to: RouteLocationResolved,
+ replace?: boolean,
redirectedFrom?: RouteLocation
): Promise<NavigationFailure | void | undefined> {
- const targetLocation: RouteLocation = (pendingLocation = resolve(to))
+ replace = to.replace ?? replace
+ pendingLocation = to
const from = currentRoute.value
const data: HistoryState | undefined = (to as RouteLocationOptions).state
const force: boolean | undefined = (to as RouteLocationOptions).force
- const replace = targetLocation.replace ?? _replace
- console.log({ replace })
- const shouldRedirect = handleRedirectRecord(targetLocation)
+ const shouldRedirect = handleRedirectRecord(to)
- if (shouldRedirect)
+ if (shouldRedirect) {
return pushWithRedirect(
- assign(locationAsObject(shouldRedirect), {
+ {
+ // @ts-expect-error: FIXME: refactor location types
+ ...resolve(shouldRedirect, currentRoute.value),
state:
typeof shouldRedirect === 'object'
? assign({}, data, shouldRedirect.state)
: data,
force,
- }),
+ },
replace,
// keep original redirectedFrom if it exists
- redirectedFrom || targetLocation
+ redirectedFrom || to
)
+ }
// if it was a redirect we already called `pushWithRedirect` above
- const toLocation = targetLocation as RouteLocationNormalized
+ const toLocation = to as RouteLocationNormalized
toLocation.redirectedFrom = redirectedFrom
let failure: NavigationFailure | void | undefined
- if (!force && isSameRouteLocation(stringifyQuery, from, targetLocation)) {
+ if (!force && isSameRouteLocation(stringifyQuery, from, to)) {
failure = createRouterError<NavigationFailure>(
ErrorTypes.NAVIGATION_DUPLICATED,
{ to: toLocation, from }
// we are redirecting to the same location we were already at
isSameRouteLocation(
stringifyQuery,
+ // @ts-expect-error: FIXME: failure.to should not contain relative locations
resolve(failure.to),
toLocation
) &&
}
return pushWithRedirect(
- // keep options
- assign(locationAsObject(failure.to), {
+ {
+ // @ts-expect-error: FIXME: refactor location types
+ ...resolve(shouldRedirect, currentRoute.value),
state:
typeof failure.to === 'object'
? assign({}, data, failure.to.state)
: data,
force,
- }),
+ },
// preserve an existing replacement but allow the redirect to override it
replace,
// preserve the original redirectedFrom if any
const shouldRedirect = handleRedirectRecord(toLocation)
if (shouldRedirect) {
pushWithRedirect(
- assign(shouldRedirect, { force: true }),
+ assign(
+ // @ts-expect-error: FIXME: refactor location types
+ resolve(shouldRedirect),
+ { force: true }
+ ),
true,
toLocation
).catch(noop)
// the error is already handled by router.push we just want to avoid
// logging the error
pushWithRedirect(
- assign(locationAsObject((error as NavigationRedirectError).to), {
- force: true,
- }),
+ assign(
+ resolve(
+ // @ts-expect-error: to should be an absolute location
+ (error as NavigationRedirectError).to
+ ),
+ {
+ force: true,
+ }
+ ),
undefined,
toLocation
// avoid an uncaught rejection, let push call triggerError
hasRoute,
getRoutes,
+ // @ts-expect-error FIXME: update EXPERIMENTAL_Router types
resolve,
options,
+ // @ts-expect-error FIXME: update EXPERIMENTAL_Router types
push,
+ // @ts-expect-error FIXME: update EXPERIMENTAL_Router types
replace,
go,
back: () => go(-1),