stripBase,
isSameRouteLocationParams,
isSameRouteLocation,
+ resolveRelativePath,
} from '../src/location'
import { RouteLocationNormalizedLoaded } from 'src'
+import { mockWarn } from 'jest-mock-warn'
describe('parseURL', () => {
let parseURL = originalParseURL.bind(null, parseQuery)
).toBe(true)
})
})
+
+describe('resolveRelativePath', () => {
+ mockWarn()
+ it('resolves relative direct path', () => {
+ expect(resolveRelativePath('add', '/users/posva')).toBe('/users/add')
+ expect(resolveRelativePath('add', '/users/posva/')).toBe('/users/posva/add')
+ expect(resolveRelativePath('add', '/users/posva/thing')).toBe(
+ '/users/posva/add'
+ )
+ })
+
+ it('resolves relative direct path with .', () => {
+ expect(resolveRelativePath('./add', '/users/posva')).toBe('/users/add')
+ expect(resolveRelativePath('./add', '/users/posva/')).toBe(
+ '/users/posva/add'
+ )
+ expect(resolveRelativePath('./add', '/users/posva/thing')).toBe(
+ '/users/posva/add'
+ )
+ })
+
+ it('resolves relative path with ..', () => {
+ expect(resolveRelativePath('../add', '/users/posva')).toBe('/add')
+ expect(resolveRelativePath('../add', '/users/posva/')).toBe('/users/add')
+ expect(resolveRelativePath('../add', '/users/posva/thing')).toBe(
+ '/users/add'
+ )
+ })
+
+ it('resolves multiple relative paths with ..', () => {
+ expect(resolveRelativePath('../../add', '/users/posva')).toBe('/add')
+ expect(resolveRelativePath('../../add', '/users/posva/')).toBe('/add')
+ expect(resolveRelativePath('../../add', '/users/posva/thing')).toBe('/add')
+ expect(resolveRelativePath('../../../add', '/users/posva')).toBe('/add')
+ })
+
+ it('works with the root', () => {
+ expect(resolveRelativePath('add', '/')).toBe('/add')
+ expect(resolveRelativePath('./add', '/')).toBe('/add')
+ expect(resolveRelativePath('../add', '/')).toBe('/add')
+ expect(resolveRelativePath('.././add', '/')).toBe('/add')
+ expect(resolveRelativePath('./../add', '/')).toBe('/add')
+ expect(resolveRelativePath('../../add', '/')).toBe('/add')
+ expect(resolveRelativePath('../../../add', '/')).toBe('/add')
+ })
+
+ it('ignores it location is absolute', () => {
+ expect(resolveRelativePath('/add', '/users/posva')).toBe('/add')
+ })
+
+ it('warns if from path is not absolute', () => {
+ resolveRelativePath('path', 'other')
+ resolveRelativePath('path', './other')
+ resolveRelativePath('path', '../other')
+
+ expect('Cannot resolve').toHaveBeenWarnedTimes(3)
+ })
+})
RouteParamValue,
} from './types'
import { RouteRecord } from './matcher/types'
+import { warn } from './warning'
/**
* Location object returned by {@link `parseURL`}.
* Check if two arrays are the same or if an array with one single entry is the
* same as another primitive value. Used to check query and parameters
*
- * @param a array of values
- * @param b array of values or a single value
+ * @param a - array of values
+ * @param b - array of values or a single value
*/
function isEquivalentArray<T>(a: T[], b: T[] | T): boolean {
return Array.isArray(b)
? a.length === b.length && a.every((value, i) => value === b[i])
: a.length === 1 && a[0] === b
}
+
+/**
+ * Resolves a relative path that starts with `.`.
+ *
+ * @param to - path location we are resolving
+ * @param from - currentLocation.path, should start with `/`
+ */
+export function resolveRelativePath(to: string, from: string): string {
+ if (to.startsWith('/')) return to
+ if (__DEV__ && !from.startsWith('/')) {
+ warn(
+ `Cannot resolve a relative location without an absolute path. Trying to resolve "${to}" from "${from}". It should look like "/${from}".`
+ )
+ return to
+ }
+
+ const fromSegments = from.split('/')
+ const toSegments = to.split('/')
+
+ let position = fromSegments.length - 1
+ let toPosition: number
+ let segment: string
+
+ for (toPosition = 0; toPosition < toSegments.length; toPosition++) {
+ segment = toSegments[toPosition]
+ // can't go below zero
+ if (position === 1 || segment === '.') continue
+ if (segment === '..') position--
+ // found something that is not relative path
+ else break
+ }
+
+ return (
+ fromSegments.slice(0, position).join('/') +
+ '/' +
+ toSegments
+ .slice(toPosition - (toPosition === toSegments.length ? 1 : 0))
+ .join('/')
+ )
+}